首页 > 学院 > 开发设计 > 正文

《C++编码规范》修订说明

2019-11-17 05:26:46
字体:
来源:转载
供稿:网友

  目录p11:

原则23.22 代码中用到的路径只用“/”而不要用“/”··· 196

更改为

原则23.22 代码中用到的路径只用“/”而不要用“”··· 196

正文P1倒数第2行:

多态:父类和子类可以为同一个接口(复用)提供不同的实现,外部代码不需任何改动(复用)就可以拥有不同的特性。

更改为

多态:不同子类可以为同一个父类接口(复用)提供不同的实现,外部代码不需任何改动(复用)就可以拥有不同的特性。



正文P6原则1.2 关于变量和函数名

1.2.1 说明

……(同时参阅:“原则1.7 关于匿名命名空间级标识符的前缀”、……)

更改为

……(同时参阅:“原则1.7 关于全局命名空间级标识符的前缀”、……)

正文P9原则1.4 关于宏、常量和模板

1.4.2 例子

// 常量类的宏

#define PIE 3.1415926

// 函数类的宏

#define MAX( a,b ) (/* … */)

// 常量

const int LENGTH = 1024;

// 枚举中的常量成员

更改为

// 常量类的宏PIE

#define PIE 3.1415926

// 函数类的宏MAX

#define MAX( a,b ) (/* … */)

// 常量LENGTH

const int LENGTH = 1024;

// 枚举中的常量成员BLUE, RED, WHITE

(还有几处类似的添加)

正文P12原则1.7上面

合理的命名方式肯定在这两个极端中间的某处,但具体在哪里有赖于大家自己的判定。

更改为

合理的命名方式肯定在这两个极端中间的某处,但具体在哪里有赖于大家自己的判定。不过,一个组织或部门内部最好统一意见,以方便大家互相理解交流。



正文P12原则1.7 关于匿名命名空间级标识符的前缀

原则1.7 关于匿名命名空间级标识符的前缀

1.7.1 说明

给匿名命名空间级标识符一个公共前缀(如所属Package名或Library名,加下划线),用来区别其他提供类似功能的Packet或Library等。

匿名命名空间中的标识符指的是全局或文件级变量名、常量名、宏、类型名、函数名等。

更改为

原则1.7 关于全局命名空间级标识符的前缀

1.7.1 说明

给全局命名空间(匿名,全局变量缺省所属的那个命名空间,以下同)级标识符一个公共前缀(如所属Package名或Library名,加下划线),用来区别其他提供类似功能的Packet或Library等。

全局命名空间中的标识符指的是全局或文件级变量名、常量名、宏名、类型名、函数名等。

正文P13原则1.7 关于匿名命名空间级标识符的前缀

1.7.3 原因

假如希望代码复用,则匿名命名空间级标识符就需要防止命名冲突。

更改为

假如希望代码复用,则全局命名空间级标识符就需要防止命名冲突。



正文P13原则1.8 减少匿名命名空间级标识符

原则1.8 减少匿名命名空间级标识符

1.8.1 说明

尽量减少匿名命名空间级变量、常量、宏及函数等标识符。……

更改为

原则1.8 减少全局命名空间级标识符

1.8.1 说明

尽量减少全局命名空间级变量、常量、宏及函数等标识符。……

正文P13原则1.8 减少匿名命名空间级标识符

……

1.8.2 例子

class CommonDefinition_T …

{

public :

const float PIE;

// …

};

更改为

1.8.2 例子

class CommonDefinition_T …

{

public :

const float PIE; // 现在常量PIE不再是全局的

// …

};

正文P13原则1.8 减少匿名命名空间级标识符

1.8.3 原因

……

缩短生命周期,使之只存在于它应该发挥作用的有限时间内,从而减少麻烦:更好记、更好维护、不会被误用或滥用等。

更改为

缩小其有效周期/范围,使之只存在于它应该发挥作用的有限时间/空间内,从而减少麻烦:更好记、更好维护、不会被误用或滥用等。

正文P16原则1.12 尽量用英文命名


英语是最通用的语言,非凡是在程序语言中,其他语言(比如选择汉语拼音)可能造成阅读者理解上的困难。

更改为

英语是最通用的语言,非凡是在程序语言中,其他语言(比如选择汉语拼音)可能造成阅读者理解上的困难,也不利于更大范围的代码复用。

正文P18原则1.19 避免名字中出现外形混淆的字母或数字

例子

/*

* 字母O和数字0外形类似,避免混用;实在无

* 法避免,最好总是用小写,这样和数字0区别还

* 大一点

*/

更改为

例子

/*-----------------------------------------------------------------

* 字母O和数字0外形类似,避免混用;实在无

* 法避免,字母O最好总是用小写,这样和数字0

* 区别还大一点

*-----------------------------------------------------------------*/

(为了区分轻易,本书所有的/*  */的注释均在上下加了短横线--------)

正文P26原则2.3 少用浮点数除非必须

原则2.3 少用浮点数除非必须

更改为

原则2.3 少用浮点类型除非必须

C++编码规范》修订说明

正文P26原则2.3 少用浮点数除非必须

2.3.1 例子

int baudRate = 9600;

int symbolsIn15msec;

// 使用了浮点数,能避免吗

更改为

2.3.1 例子

int baudRate = 9600;

int symbolsIn15msec;

// 使用了浮点类型,能避免吗

正文P26原则2.3 少用浮点数除非必须

2.3.2 原因

……

浮点数的异常处理复杂(比如上溢、下溢等的处理)。

……

更改为

2.3.2 原因

……

浮点类型的异常处理复杂(比如上溢、下溢等的处理)。

……

正文P26原则2.4 用typedef简化程序中的复杂语法

2.4.1 例子

// 用typedef简化函数指针

typedef int (*CallbackFuncPtr_T)(int parameter);

更改为

2.4.1 例子

// 用typedef简化函数指针

typedef int (*CallbackFunctionPtr_T)(int parameter);



正文P26原则2.4 用typedef简化程序中的复杂语法

2.4.3 定量分析的参考

包含4个以上独立元素的语法应被视为复杂语法,如上例中的函数指针定义,不算typedef的独立元素数为5。

更改为

2.4.3 定量分析的参考

包含4个以上独立元素的语法应被视为复杂语法,如上例中的函数指针定义,不算typedef的独立元素数为5(int,*,CallbackFunctionPtr_T,int,parameter)。

正文P28

2.7.2 原因

……

减少匿名命名空间级变量、常量、宏及函数。

更改为

符合“原则1.8 减少全局命名空间级标识符”。



正文P30

原则3.1 一定要做到先定义后使用

3.1.1 说明

C++必须这样做(否则编译通不过)。C程序没有强制要求,但也应该先提供原型,再使用函数。

3.1.2 原因

先定义使得编译器能够在编译时就检查和找出错误(而不是等到连接或运行时)。

更改为

原则3.1 函数一定要做到先声明后使用

3.1.1 说明

C++必须这样做(否则编译通不过)。C程序没有强制要求,但也应该先提供原型,再使用函数。

3.1.2 原因

先声明使得编译器能够在编译时就检查和找出错误(而不是等到连接或运行时)。

正文P40原则3.17 当函数返回引用或指针时,用文字描述其有效期

3.17.1 说明

有效期是指引用或指针能有效地找到对象的期间。文字描述最好以注释形式放在函数所在的头文件中(这样比较直接)。

更改为

3.17.1 说明

有效期是指引用或指针能有效地找到目标的时间段。文字描述最好以注释形式放在函数所在的头文件中(这样比较直接)。

正文P41

3.18.2 原因

……


返回只读(常量)的引用或指针还是可接受的。

更改为

关于能否返回只读(常量)的引用或指针,请参见“原则8.3 类成员可以转换成常量形式暴露出来”。

正文P41~42

3.20.1 说明

假如我们能把友元函数定义成虚函数,则子类既可以继续该友元的接口而无需重复声明友元……

更改为

假如我们能把友元函数定义成虚函数,则子类既可以继续该函数而无需重复声明友元……

正文P45原则4.2 类成员应是私有的(PRivate)

4.2.2 原因

更糟的是,任何对类的修改都将影响使用该类的代码,因为这些代码有权直接访问(被修改的)类成员。

更改为

更糟的是,任何对类实现的修改都可能影响使用该类的代码,因为这些代码有权直接访问(被修改的)类成员。

正文p47原则4.5 降低类间的耦合度

4.5.2 例子

经常看到这样的例子:一个大功能模块中有许多不同类型的对象,为了协同工作,每类对象都多少带有其他对象的指针或引用,造成你中有我,我中有你,最终导致一锅粥,谁也离不开谁。

更改为

4.5.2 例子

经常看到这样的例子:一个大功能模块中有许多不同类型的对象,为了协同工作,每类对象内部都多少带有其他对象的指针或引用,造成你中有我,我中有你,最终导致一锅粥,谁也离不开谁。

正文p50原则4.9 避免为每个类成员提供访问函数

原因

……

专注于属性的类在多线程环境下效率低下。若专注于行为,一次行为会修改一连串相关属性而只需上一次锁(在一个成员函数/行为中);而专注于属性,则不得不每修改一个属性就上一次锁(在每个类成员提供访问函数中),因为要防止多个线程同时调用访问函数(去修改同一个属性)。

更改为

……

专注于属性的类在多线程环境下效率低下。若专注于行为,一次行为会修改一连串相关属性而只需上一次锁(在一个成员函数中);而专注于属性,则不得不每修改一个属性就上一次锁(在每个类成员的访问函数中),因为要防止多个线程同时调用访问函数(去修改同一个属性)。

正文p54

原则4.16 用嵌套类的方法减少匿名命名空间类的数量

4.16.1 说明

不需要把所有类的定义都放在匿名命名空间中。嵌套在别的类中的类是不属于匿名命名空间的。

更改为

原则4.16 用嵌套类的方法减少全局命名空间类的数量

4.16.1 说明

不需要把所有类的定义都放在全局命名空间中。嵌套在别的类中的类是不属于全局命名空间的。



正文p55

第5章 (面向对象的)继续

继续是面向对象语言的一个基本特性。这是一个强大的功能,但必须运用得当,否则发挥不出其特点,甚至适得其反。

更改为

继续是面向对象语言的一个基本特性。功能强大自不必多言,但“打破封装”的副作用却鲜为人知,所以必须运用得当,否则发挥不出其特点,甚至适得其反。

正文p55原则5.2 关于“有”和“由…实现”

5.2.1 说明

(一个类)包含(另一个类)意味着“有一个”或“由…实现”。“私有继续(private inheritance)”意味着“由…实现”。

更改为

5.2.1 说明

(一个类)包含(另一个类)意味着“有一个”或“由…实现”。“私有继续(private inheritance)”意味着“由…实现”(和“包含”类似;完全不同于“公共继续”)。

正文p59

5.5.2 原因

……

与此类似,同层类的个数也不能太多,否则应考虑是否要加一个父类,以便做某种程度上的(新的)抽象,从而减少同层类的个数(这是一种平衡的艺术)。

更改为

与此类似,派生于同一父类的子类个数也不能太多(计划生育?),否则应考虑是否要加一层父类,通过某种程度上的(新的)抽象,减少同层类的个数(这是一种平衡的艺术)。

正文p59

原则5.6 继续树上非叶子节点的类应是虚基类

5.6.1 例子

假如你有两个(非虚)类C1和C2,且希望C2派生于C1,如图5-1左边所示,则应该增加一个虚基类A,且将C1和C2都从A派生出来,如图5-1右边所示。

更改为

原则5.6 继续树上非叶子节点的类应是抽象基类

5.6.1 例子

假如你有两个(非抽象)类C1和C2,且希望C2派生于C1,如图5-1左边所示,则应该增加一个抽象基类(abstract base class)A,且将C1和C2都从A派生出来,如图5-1右边所示。

正文p60

……

进一步说,因为C++的类封装了具体实现,而只把接口露在外面,所以C1的接口应该是C2最想要继续的。换句话说,只要C2继续了C1的接口,C2的对象就可以被看作C1的对象而参与任何C1对象可参与的活动。由此可见,接口本身(而不是具体实现)是参与活动的充分必要条件,这就是为什么A通常是虚基类。

让我们再换一个角度,继续的一大缺点是打破封装,即把基类的实现细节暴露给派生类。导致的结果是,对基类的修改将无法保证不会波及派生类,即基类和派生类是紧耦合关系(紧耦合的缺点请参见“原则4.5 降低类间的耦合度”),除非基类是纯虚的(没有实现细节)。


另外,为了防止多重继续带来的各种问题(参见“原则5.14 慎用多重继续”),也建议继续树上非叶子节点的类尽量是没有成员的纯虚基类(类似于java中的Interface)。

更改为

进一步说,因为C++的类封装了具体实现,而只把接口露在外面,所以C1的接口应该是C2最想要继续的。换句话说,只要C2继续了C1的接口,C2的对象就可以被看作C1的对象而参与任何C1对象可参与的活动。由此可见,接口本身(而不是具体实现)是参与活动的充分必要条件,这就是为什么A通常是只定义接口的完全的抽象基类(类似于Java中的Interface)。

用完全抽象基类的另一个原因:继续存在一个很大的缺陷,就是打破封装,即把基类的实现细节暴露给派生类。导致的结果是,对基类的修改将无法保证不会波及派生类,即基类和派生类是紧耦合关系(紧耦合的缺点请参见“原则4.5 降低类间的耦合度”),除非基类完全没有实现细节。

用完全抽象基类的第三个原因:为了防止多重继续带来的各种问题(参见“原则5.14 慎用多重继续”),继续树上非叶子节点的类最好没有成员。

正文p73

6.8.3 原因

防止其后再次使用该指针。

更改为

防止其后再次使用该指针(使用NULL会马上导致系统错误,好查)。

正文p85原则7.19 将循环索引的初值定在循环点四周

7.19.1 例子

假设循环队列下标从0到MAX-1(MAX为该队列可容纳的元素个数),则队首应设在MAX-3处。

7.19.2 原因

循环索引最常出问题的地方就在循环点四周,将队首放在这里可以很早就发现问题。

问题常出现在:插入元素使得下标从MAX-1绕回到0、删除元素使得下标从0绕回到MAX-1、插入头一两个元素,以及删除最后一两个元素时。选择MAX-3可以一口气将这些情况都覆盖住。

更改为

7.19.1 例子

假设循环队列下标从0到MAX-1(MAX为该队列可容纳的元素个数),则队首游标应设在MAX-3处。

7.19.2 原因

循环索引最常出问题的地方就在循环点四周,将队首游标放在这里可以很早就发现问题。

问题常出现在:插入元素使得游标从MAX-1绕回到0、删除元素使得游标从0绕回到MAX-1、插入头一两个元素,以及删除最后一两个元素时。选择MAX-3可以一口气将这些情况都覆盖住。

正文p89

原则8.1 关于常量修饰符的含义

例子

char* p = “Hello.”; // 指针不是常量,指针指向的也不是常量

const char* p = “Hello.”; // 指针不是常量,指针指向的是常量

char* const p = “Hello.”; // 指针是常量,指针指向的不是常量

const char* const p = “Hello”; // 指针是常量,指针指向的也是常量

更改为

原则8.1 关于常量修饰符的含义

8.1.1 例子

char* p = “Hello.”; // 指针不是常量,指针指向的也不是常量

char const* p = “Hello.”; // 指针不是常量,指针指向的是常量

char* const p = “Hello.”; // 指针是常量,指针指向的不是常量

char const* const p = “Hello”; // 指针是常量,指针指向的也是常量

8.1.2 说明

“char* p”的意思是:p是一个指针;它指向字符类型。

“char const* p”的意思是:p是一个指针;它指向一个常量;该常量是字符类型。

“char* const p”的意思是:p是一个常量;它是一个指针常量;该常量指针指向字符类型。

“char const* const p”的意思是:p是一个常量;它是一个指针常量;该常量指针指向一个常量;而被指向的常量是字符类型。

正文p89~90

8.2.3 原因

编译器会永远记住此事。假如今后对blockCopy()的任何修改(有意或无意)要对pSrc所指数据进行写操作,就会引起编译错误,从而提示该函数的后继开发者遵循最初的设计。

更改为

编译器会永远记住此事。假如今后对blockCopy()函数体的任何修改(有意或无意)要对pSrc所指数据进行写操作,就会引起编译错误,从而提示该函数的后继开发者遵循最初的设计。

正文p92

8.5.2 原因

造成程序状态改变的成员函数不是绝对意义上的常量成员函数(不符合大多数人对常量成员函数的预期)。

更改为

造成程序状态改变的成员函数不是绝对意义上的常量成员函数。虽然不一定违反C++语法,但语义有问题,且不符合大多数人对常量成员函数的预期。



正文p123原则15.4 不要用分号结束宏定义

例子

/*

* 用分号结尾,结果导致下面第二条语句变成

* “channel=CHANNEL_MAX;-1;”,编译出错


*/

更改为

例子

/*----------------------------------------

* 用分号结尾,结果导致下面第二条语句变成

* “channel=16;-1;”,编译出错

-----------------------------------------*/

正文p128

15.11.2 原因

……

对于不得不出现在公共头文件中的宏(如防止头文件被多次引用的宏),要加前缀以减少冲突,参见“原则1.7 关于匿名命名空间级标识符的前缀”。

更改为

……

对于不得不出现在公共头文件中的宏(如防止头文件被多次引用的宏),要加前缀以减少冲突,参见“原则1.7 关于全局命名空间级标识符的前缀”。

正文p132原则16.7 非凡当心析构时发生异常

原因

C++的异常处理机制规定,若在异常处理期间又发生异常,程序将被终止。

更改为

C++的异常处理机制规定,若在异常处理期间(catch语句块中)又发生异常,程序将被终止。

正文p142

17.15.2 例子

// {}单独占一行,并且和if语句缩进相同

更改为

17.15.2 例子

// {}单独占一行,并且和if/for语句缩进相同

正文p146原则17.20 为所有switch语句提供default分支

原因

……这样,今后一旦增加新分支(比如枚举增加了新的值),不必担心是否会遗漏相关的switch语句。

更改为

原因

……这样,今后一旦增加新分支(比如枚举增加了新的值),不必担心是否会遗漏相关的switch语句(遗漏会造成程序退出,马上就能发现)。

正文p148

17.24.2 原因

编译器在解析变量定义时是从右向左扫描的,编程者也要习惯这种方式。如上例所示,pName首先是一个指针,其次它要指向一个常量,第三这个常量是字符类型的。仔细品味这些修饰,你会发现它们是有优先顺序的,优先级越高越要放在右面(紧挨变量名)。

更改为

17.24.2 原因

编译器在解析变量定义时是以变量名为核心向外扫描的,编程者也要习惯这种方式。如上例所示,pName首先是一个指针,其次它要指向一个常量,第三这个常量是字符类型的。仔细品味这些修饰,你会发现它们是有优先顺序的,优先级越高越要靠近变量名。(还可参考“原则8.1 关于常量修饰符的含义”)

正文p160

原则18.12 区分“战略性”注释和“战术性”注释

18.12.1 说明

注释分“战略性”和“战术性”两大类:

“战略性”注释用来说明一段代码,因而要放在该段代码之前。通常,此类注释较长(超过一行),建议用/**/风格。

“战术性”注释用来说明一句代码,通常放在该代码之后(行末注释)。建议用//风格。

更改为

原则18.12 区分段落注释和单行注释

18.12.1 说明

注释分段落和单行两大类:

段落注释用来说明一段代码,因而要放在该段代码之前。通常,此类注释较长(超过一行),建议用/**/风格。

单行注释用来说明一句代码,通常放在该代码之后(行末注释)。建议用//风格。

段落注释用横线上下框住(见例子或附录的大段代码),目的是一目了然地将代码和注释区分出来,这样有利于代码和注释的阅读。(单行注释这样做显得累赘,请读者自己权衡。)

正文p161原则18.15 减少不必要的单独占一行的注释

原因

……

类定义和函数前的注释(战略性)不会打断代码,因为它们在一个完整的逻辑段之外。由此也可以揣摩该原则进退的分寸。

更改为

原因

……

类定义和函数前的注释(段落注释)不会打断代码,因为它们在一个完整的逻辑段之外。由此也可以揣摩该原则进退的分寸。

正文p172

20.1.4 此类宏的命名方式

……

此类宏的前缀请参见“原则1.7 关于匿名命名空间级标识符的前缀”。

更改为

……

此类宏的前缀请参见“原则1.7 关于全局命名空间级标识符的前缀”。

正文p174原则20.7 将函数库放在一个单独的目录下引用

20.7.1 例子

#include “MyLibrary/ZErrLog.hpp”

更改为

20.7.1 例子

#include “Z_Library/ZErrLog.hpp”

正文p208

24.15.1 说明

……

但有的编译器会视满足下述条件之一的类为非直传类:


……

有虚基类。

更改为

24.15.1 说明

……

但有的编译器会视满足下述条件之一的类为非直传类:

……

有抽象基类。



正文p213

25.1.2 原因

减少命名冲突:

具体说明,请参见“原则1.8 减少匿名命名空间级标识符”。

更改为

25.1.2 原因

减少命名冲突:

具体说明,请参见“原则1.8 减少全局命名空间级标识符”。

P220附录

/*

* ConstrUCtor:

* Direct initialization.

*/

Z_SmartPtr_T(

POINTEE_T* pPointee,

Z_AbstractFactory_T<POINTEE_T>* pFactory=NULL(2)(3)

);

更改为

/*------------------------

* Constructor:

* Direct initialization.

-------------------------*/

eXPlicit Z_SmartPtr_T(

POINTEE_T* pPointee,

Z_AbstractFactory_T<POINTEE_T>* pFactory=NULL(2)(3)

);

p222附录

// Program Notes: -Multi-thread safe = False:

// This is because these kind of classes is just like a

// dumb pointer which need to be protected outside.

更改为

// Program Notes: -Multi-thread safe = False:

// This is because this kind of classes is just like dumb

// pointers which need to be protected outside.

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表