您的位置:首页 > 编程语言 > C语言/C++

阅读《高质量C++C编程指南》和《google_c++编程风格(高清版)》笔记

2015-04-09 19:01 197 查看
在整理资料的时候,翻出这个笔记,方便以后自己回忆。这2本挺不错的,建议C++入门一段时间了的同学可以去看看。

-------------------------------------------- 笔记部分,随便记得 --------------------------------------------

1.

应使用初始化代替声明 + 赋值的方式。

int i;

i = f(); // 坏——初始化和声明分离

int i = g(); // 好——初始化时声明

2.

如果发量是一个对象,每次迕入作用域都要调用其极造函数,每次退出作用域都要调用其枂极函数。

// 低效的实现

for (int i = 0; i <1000000; ++i)

{

Foo f; // 极造函数和枂极函数分别调用 1000000 次!f.DoSomething(i);

}

类似发量放到循环作用域外面声明要高效的多:

Foo f; // 极造函数和枂极函数叧调用 1 次 for(int i = 0; i < 1000000; ++i) {

f.DoSomething(i); }

3.

禁止使用 class 类型的全尿发量(包括 STL 的 string,vector 等等),因为它们的初始化顺序有可能 导致极造出现问题。内建类型和由内建类型极成的没有极造函数的结极体可以使用,如果你一定要使用 class 类型的全尿发量,请使用单件模式(singleton pattern)。

4.

嵌套类符吅尿部使用原则,叧是丌能在其他头文件中前置声明,尽量不要 public;

5.

对单参数构造函数使用C++关键字explicit。

6.

仅当只有数据时使用struct,其它一概使用class。

7.

存取函数的定义一般内联在头文件中。例如get set函数

8.

定义次序如下:public:、protected:、private:,如果那一块没有,直接忽略即可。

每一块中,声明次序一般如下:

1) typedefs和enums;

2) 常量;

3) 构造函数;

4) 析构函数;

5) 成员函数,含静态成员函数;

6) 数据成员,含静态数据成员。

宏DISALLOW_COPY_AND_ASSIGN置于private:块之后,作为类的最后部分。参考拷贝构造函数。

.cc文件中函数的定义应尽可能和声明次序一致。

9.

倾向于选择短小、凝练的函数。

长函数有时是恰当的,因此对于函数长度并没有严格限制。如果函数超过40行,可以考虑在不影响程序结构的情况下将其分割一下。

即使一个长函数现在工作的非常好,一旦有人对其修改,有可能出现新的问题,甚至导致难以发现的bugs。使函数尽量短小、简单,便于他人阅读和修改代码。

10.

函数形参表中,所有引用必须是 const:

事实上返是一个硬性约定:输入参数为值戒常数引用,输出参数为挃针;输入参数可以是常数挃针,但丌

能使用非常数引用形参。

11.

我们强烈建议你在仸何可以使用的情冴下都要使用const。

12.

C++内建整型中,唯一用到的是 int,如果程序中需要丌同大小的发量,可以使用<stdint.h>中的精确宽 度(precise-width)的整型,如int16_t。

13.

宏存储常量可以 const 发量替代;宏“缩写”长发量名可以引用替代;使用宏迕行条件 编译,返个......,最好丌要返么做,会令测试更加痛苦(#define 防止头文件重包吨当然是个例外)。

14.

整数用 0,实数用 0.0,挃针用 NULL,字符(串)用'\0'。

15.

函数命名、发量命名、文件命名应具有描述性,丌要过度缩写,类型和发量应该是名词,函数名可以用“命 令性”动词。

16.

内联函数必须放在.h 文件中,如果内联函数比轳短,就直接放在.h 中。如果代码比轳长,可以放到以-inl.h 结尾的文件中。对亍包吨大量内联代码的类,可以有三个文件:

url_table.h

url_table.cc

url_table-inl.h

参考第一篇-inl.h 文件一节。

17.

TODO 徆丌错,有时候,注释确实是为了标记一些未完成的戒完成的丌尽如人意的地方,返样一搜索, 就知道迓有哪些活要干,日志都省了。

180

有些条件诧句写在同一行以增强可诺性,叧有当诧句简单幵丏没有使用 else 子句时使用:

if (x == kFoo)return new Foo();

if (x == kBar) return newBar();

如果诧句有 else 分支是丌允许的:

// Not allowed - IF statementon one line when there is an ELSE clause

if (x) DoThis(); elseDoThat();

通常,单行诧句丌需要使用大括号,如果你喜欢也无可厚非,也有人要求 if必须使用大括号: if (condition)

DoSomething(); // 2 spaceindent.

if (condition) {

DoSomething(); // 2 spaceindent.

}

19.在声明挃针发量戒参数时,星号不类型戒发量名紧挨都可以:

// These are fine, spacepreceding.

char *c;

const string &str;

注:个人比轳习惯不发量紧挨的方式。

20.

return 表达式中不要使用囿括号。

21.

代码块头、尾丌要有空行

22.

基类 结尾加infface

23。

尽可能在定义变量的同时初始化该变量(就近原则)

24.

一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。

25.

长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以 便突出操作符)。

26.

应当将修饰符 * 和 & 紧靠变量名

27.

例如不要把CurrentValue 写成NowValue。

28.

变量的名字应当使用“名词”或者“形容词+名词”。

29.

全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。

30.

常量全用大写的字母,用下划线分割单词。

例如:

const int MAX = 100;

const int MAX_LENGTH = 100;

31.

静态变量加前缀 s_(表示 static)。

32.

如果不得已需要全局变量,则使全局变量加前缀 g_

33.

C++/C 循 环 语 句 中 , for 语 句 使 用 频 率 最 高 , while 语 句 其 次 , do 语 句 很 少 用 。

34.

在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的 循环放在最外层,以减少 CPU跨切循环层的次数

35.

如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面

36.

不可在 for 循环体内修改循环变量,防止 for 循环失去控制。

37.

建议 for语句的循环控制变量的取值采用“半开半闭区间”写法。

38.

就象楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。所以我们主

张 少 用 、 慎 用 goto语 句 , 而 不 是 禁 用 。

39.

尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或 字符串。

高质量 C++/C 编程指南,v 1.0

例如: #define constint constfloat

MAX 100 /* C语言的宏常量 */ MAX=100;

// C++语言的const常量 PI=3.14159;

// C++语言的const常量

40.

在 C++程序中只使用 const常量而不使用宏常量,即const常量完 全取代宏常量。

41.

需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。

42.

怎样才能建立在整个类中都恒定的常量呢?别指望 const数据成员了,应该用类中 的枚举常量来实现。例如

classA {⋯

enum { SIZE1 = 100, SIZE2 =200}; // 枚 举 常 量 int array1[SIZE1];

2001

Page 34 of 100

Generated by Foxit PDFCreator © Foxit Software
http://www.foxitsoftware.com
For evaluation only.

应给出一些孤立的值。 例如:

int array2[SIZE2];

};

43.

参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。

44.

参数命名要恰当,顺序要合理。一般地,应将目的参数放在前面,源参数放在后面。

45.

如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该 指针在函数体内被意外修改。

46.

如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来 传递,这样可以省去临时对象的构造和析构过程,从而提高效率。

47.

函数名字与返回值类型在语义上不可冲突。

48.

如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出 错。对于赋值函数,应当用“引用传递”的方式返回 String 对象。

49.

在函数体的“入口处”,对参数的有效性进行检查。

50.

在函数体的“出口处”,对 return 语句的正确性和效率进行检查。

51.

函数的功能要单一,不要设计多用途的函数。

52.

函数体的规模要小,尽量控制在 50 行代码之内。

53.

在函数的入口处,使用断言检查参数的有效性(合法性)。特别时指针

void *memcpy(void*pvTo,constvoid*pvFrom,size_tsize){

assert((pvTo!=NULL)&&(pvFrom!=NULL));//使用断言

byte *pbTo = (byte *) pvTo;byte *pbFrom = (byte *) pvFrom; while(size -- > 0 )

*pbTo ++ = *pbFrom ++ ;returnpvTo;

}

54.

引用的主要功能是传 递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、 指针传递和引用传递。

55.

使用 free 或 delete 释放了内存后,没有将指针设置为 NULL。导致产生“野指针”。

56.

用 malloc或 new申请内存之后,应该立即检查指针值是否为 NULL。 防 止 使 用 指 针 值 为 NULL 的 内 存 。

57.

不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右 值使用。

58.

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针

59.

(1)指针消亡了,并不表示它所指的内存会被自动释放。

(2)内存被释放了,并不表示指针会消亡或者成了NULL 指针。

60.

“野指针”不是 NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用 NULL 指针,因为用 if 语句很容易判断。但是“野指针”是很危险的,if 语句对它不起作用。

“野指针”的成因主要有两种: (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为 NULL 指针,它 的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么 将指针设置为 NULL,要么让它指向合法的内存。例如

char *p = NULL;

char *str = (char *)malloc(100);

(2)指针 p 被 free 或者 delete 之后,没有置为NULL,让人误以为 p 是个合法的指针。

61.

1)越是怕指针,就越要使用指针。不会正确使用指针,肯定算不上是合格的程序员。 (2)必须养成“使用调试器逐步跟踪程序”的习惯,只有这样才能发现问题的本质。

62。

在做数据类型转换的时候不要用(int),用static_cast<int>

63.

当 心 隐 式 类 型 转 换 导 致 重 载 函 数 产 生 二 义 性

由 于 数 字 本 身 没 有 类 型 , 将数 字 当 作 参 数 时 将 自 动 进 行 类 型 转 换 ( 称 为 隐式类型转换)

64.

重 载 与 覆 盖 成员函数被重载的特征:

( 1) 相 同 的 范 围 (
在 同 一 个 类 中 ); (2)函数名字相同;

( 3) 参 数 不 同 ;

( 4) virtual 关 键 字 可 有 可 无 。

覆盖是指派生类函数覆盖基类函数,特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同;

( 3) 参 数 相 同 ;

( 4) 基 类 函 数 必 须 有 virtual
关 键 字 。

65.

这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。 (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

66.

参数缺省值只能出现在函数的声明中,而不能出现在定义体中。

例如:

void Foo(int x=0, int y=0);

// 正 确 , 缺 省 值 出 现 在 函 数 的 声 明 中 // 错 误 , 缺 省 值 出 现 在 函 数 的 定 义 体 中

void Foo(int x=0, int y=0)

{



}

67.

如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致 函数调用语句怪模怪样。

68.

已知类 String 的原型为:

class String

{

public:

String(const char*str = NULL);

String(const String&other);//拷贝函数

~ String(void);

String&operate=(constString&other);//赋值函数

private:

char *m_data;

};

69.

不 能 被 重 载 的 运 算 符

在 C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全方面

的考虑,可防止错误和混乱。

( 1) 不 能 改 变 C++内 部 数 据 类 型( 如 int,float 等 ) 的 运 算 符。 (2)不能重载‘.’,因为‘.’在类中对任何成员都有意义,已经成为标准用法。 (3)不能重载目前 C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。 (4)对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。

70.

不宜使用内联:

(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

71.

C++编译器将自动为 A 产生四个缺省的函数,如

高质量 C++/C 编程指南,v 1.0

A(void);// 缺 省 的 无 参 数 构 造 函 数

A(const A &a);// 缺 省 的 赋 值 函 数

~A(void);// 缺 省的 析 构 函 数

A & operate =(const A &a);// 缺 省 的 拷 贝 构 造 函 数

72.

classString
{
public:
String(constchar *str = NULL); // 普 通 构 造 函 数
String(constString&other);//拷贝构造函数
~ String(void); // 析 构 函 数
String&operate=(constString&other);//赋值函数
private:

char *m_data; //用 于 保 存 字 符 串
};
73.
Stroustrup在设计 C++语言时充分考虑了这个问题 并很好地予以解决:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。 当对象被创建时,构造函数被自动执行。当对象消亡时,析构函数被自动执行。这下就 不用担心忘了对象的初始化和清除工作。
74.
如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。
75.
类的 const
常量只能在初始化表里被初始化,因为它不能在函数体内用赋值的方式来初始化
76.
拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。
String a(“hello”);

String b(“world”);

String c=a;//调用了拷贝构造函数,最好写成 c(a);
c=b; //调用了赋值函数 本例中第三个语句的风格较差,宜改写成 Stringc(a)以区别于第四个语句。
77.
若在逻辑上 A 是 B 的“一部分”(apart of),则不允许 B 从 A 派生, 而是要用 A 和其它东西组合出 B。 例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以
类 Head应该由类 Eye、Nose、Mouth、Ear 组合而成,不是派生而成。如示例 10-2-1 所 示。
class Eye {
public:
void Look(void);
};
class Nose {
public:
void Smell(void);
};
class Mouth {
public:
void Eat(void);
};

class Ear {
public:
void Listen(void);
};

// 正确的设计,虽然代码冗长。 class Head
{
public:
void Look(void) {
void Smell(void) { void Eat(void) { void Listen(void) {
private:
Eye m_eye;
m_eye.Look(); } m_nose.Smell(); } m_mouth.Eat(); } m_ear.Listen(); }
};
Nose m_nose; Mouth m_mouth; Ear m_ear;
示例 10-2-1Head 由 Eye、Nose、Mouth、Ear 组合而成
78.
使用 const 提高函数的健壮性
79.
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输 入参数本来就无需保护,所以不要加const修饰。
例如不要将函数 voidFunc1(intx)写成 voidFunc1(constintx)。同理不要将
函数 voidFunc2(Aa)写成 voidFunc2(constAa)。
80.
如果输入参数采用“指针传递”,那么加 const修饰可以防止意外地改动该指针,起 到保护作用。
81.

完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也 非常快,“值传递”和“引用传递”的效率几乎相当。
问题是如此的缠绵,我只好将“const&”修饰输入参数的用法总结一下
对 于 非 内 部 数 据 类 型的 输 入 参 数 ,应 该 将“ 值 传 递 ”的 方 式 改 为“ const引 用 传 递”,目的是提高效率。例如将 voidFunc(Aa)改为 voidFunc(constA&a)。
对 于 内 部 数 据 类 型 的输 入 参 数 ,不 要 将“ 值 传 递 ”的 方 式 改 为“ const引 用 传 递 ”。 否则既达不到提高效率的目的,又降低了函数的可理解性。例如 voidFunc(intx)不 应该改为voidFunc(constint&x)。
82.
如果返回值不是内部数据类型,将函数 AGetA(void)改写为constA&GetA(void)
的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的 “ 拷 贝 ” 还 是 仅 返 回 “ 别 名 ”就 可 以 了 , 否 则 程 序 会 出 错 。
84.
任何不会修改数据成员的函数都应该声明为 const类型。
85.
当心数据类型转换发生错误。尽量使用显式的数据类型转换(让人们知道发生了什么事),避免让编译器轻悄悄地进行隐式的数据类型转换。

注:红色的部分是自己要特别留意的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: