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

在整理资料的时候,翻出这个笔记,方便以后自己回忆。这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.

当心数据类型转换发生错误。尽量使用显式的数据类型转换(让人们知道发生了什么事),避免让编译器轻悄悄地进行隐式的数据类型转换。



 注:红色的部分是自己要特别留意的


目 录 前 言6 第1 章 文件结构 1.1 权和本的声明. 1.2 头文件的结构. 1.3 定义文件的结构. 1.4 头文件的作用. 1.5 目录结构. 第2 章 程序的式 2.1 空行. 2.2 代码行. 2.3 代码行内的空格. 2.4 对齐. 2.5 长行拆分. 2.6 修饰符的位置. 2.7 注释. 2.8 类的式. 第3 章 命名规则 3.1 共性规则. 3.2 简单的WINDOWS 应用程序命名规则. 3.3 简单的UNIX 应用程序命名规则 第4 章 表达式和基本语句 4.1 运算符的优先级. 4.2 复合表达式. 4.3 IF 语句 4.4 循环语句的效率. 4.5 FOR 语句的循环控制变量. 4.6 SWITCH 语句. 4.7 GOTO 语句. 第5 章 常量 5.1 为什么需要常量. 5.2 CONST 与 #DEFINE 的比较. 5.3 常量定义规则. 5.4 类中的常量. 第6 章 函数设计 高质量C++/C 编程指南,v 1.0 2001 Page 4 of 101 6.1 参数的规则. 6.2 返回值的规则. 6.3 函数内部实现的规则. 6.4 其它建议. 6.5 使用断言. 6.6 引用与指针的比较. 第7 章 内存管理 7.1 内存分配方式 7.2 常见的内存错误及其对策 7.3 指针与数组的对比 7.4 指针参数是如何传递内存的? 7.5 FREE 和DELETE 把指针怎么啦? 7.6 动态内存会被自动释放吗?. 7.7 杜绝“野指针”. 7.8 有了MALLOC/FREE 为什么还要NEW/DELETE ?. 7.9 内存耗尽怎么办?. 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点. 7.12 一些心得体会 第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的重载、覆盖与隐藏. 8.3 参数的缺省值. 8.4 运算符重载. 8.5 函数内联. 8.6 一些心得体会. 第9 章 类的构造函数、析构函数与赋值函数 9.1 构造函数与析构函数的起源. 9.2 构造函数的初始化表. 9.3 构造和析构的次序. 9.4 示例:类STRING 的构造函数与析构函数 9.5 不要轻视拷贝构造函数与赋值函数. 9.6 示例:类STRING 的拷贝构造函数与赋值函数 9.7 偷懒的办法处理拷贝构造函数与赋值函数. 9.8 如何在派生类中实现类的基本函数. 9.9 一些心得体会. 第10 章 类的继承与组合. 高质量C++/C 编程指南,v 1.0 2001 Page 5 of 101 10.1 继承 10.2 组合 第11 章 其它编程经验. 11.1 使用CONST 提高函数的健壮性 11.2 提高程序的效率 11.3 一些有益的建议 参考文献 附录A :C++/C 代码审查表. 附录B :C++/C 试题. 附录C :C++/C 试题的答案与评分标准.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值