c++编码规范

原创 2006年06月12日 12:49:00

一,命名原则
1.1 关于类型名
类型名中每个英文单词的首字母大写,最后以_T结尾。

1.2 关于变量和函数名
首字母小写,以后每个单词的首字母大写。

1.3 关于全大写的函数名
有一类函数,调用普通函数,只是多了对于错误返回的一般化处理。这类函数可以用普通
函数的全大写名字。

1.4 关于宏,变量和模板名
全部用大写,多个单词用下划线分隔。

1.5 关于指针标识符名,建议以p开头或Ptr结尾

1.6 关于变量名前缀
i_ 类内数据成员 instance scope
c_ 类内静态数据成员 class scope
g_ 全局变量 global scope
f_ 文件变量 file scope

1.7 关于全局命名空间级标识符的前缀,最好加一个公共前缀以区分。

1.8 减少全局命名空间级标识符,可统一放到一个类中。

1.9 命名时避免使用国际组织占用的格式

1.10 名字清楚简单

1.11 尽量用可发音名字

1.12 尽量用英文命名

1.13 尽量选择通用词汇

1.14 避免用缩写

1.15 避免用会引起误解的词汇

1.16 减少名字中的冗余信息

1.17 建议起名尽量通俗

1.18 名字最好尽可能精确表达其内容

1.19 避免名字中出现混淆的数字或字母

1.20 命名类和成员使得object.method()有意义

1.21 类和对象名应是名词

1.22 实现行为的类成员函数名应是动词

1.23 类的存取和查询成员函数名应是名词或形容词

1.24 变量名是名词

1.25 布尔型的名字要直观

1.26 关于函数的左值参数用lhs,右值参数用rhs

1.27 避免局部名和外层的名字冲突

1.28 用a, an, any区分重名参数

1.29 模板类型名应有意义 template < class TYPE, int SIZE >


二,类型的使用

2.1 避免隐式声明类型

2.2 慎用无符号类型

2.3 少用浮点类型除非必须

2.4 用typedef简化程序中的复杂语法,常用于函数指针的定义。

2.5 少用union

2.6 慎用位操作

2.7 用enum取代一组常量

2.8 使用内置bool类型

2.9 尽量用引用取代指针,以NULL是否有效为判断。

三,函数

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

3.2 函数原型声明放在一个头文件中

3.3 函数无参数一定要使用void

3.4 对于内置类型参数应传值,除非要修改参数内容

3.5 对于非内置类型参数应传递引用或指针

3.6 关于何时用指针传递参数,如果函数内部要将参数以指针形式传给其他函数的,或者
参数是被new出来,要在函数内释放的,用指针。

3.7 避免使用参数不确定的函数

3.8 如果不得不使用,用提供的方法,va_start, va_list, va_arg, _va_end

3.9 避免函数的参数过多,一般最多为5个

3.10 尽量保持函数只有唯一出口

3.11 显式定义返回类型

3.12 非void,任何情况下都要有返回值

3.13 如果函数返回状态,尝试用enum作为返回类型

3.14 返回指针类型的函数应该用NULL表示出错。

3.15 函数尽量返回引用

3.16 如果必须返回值,不要强行返回引用,防止返回局部变量的引用

3.17 当函数返回引用或指针,用文字描述其有效期。

3.18 禁止成员函数返回成员的引用或指针

3.19 重复使用的代码用函数替代

3.20 关于虚友元函数,c++不支持,间接实现如下:
class Base { public: virtual void print(ostream& out); }
class Dervie : public Base { public: virtual print(ostream& out); }
ostream& operator<< (ostream& out, Base& anObject) { anObject.print(out); }

3.21 关于虚构造函数,c++不支持,间接实现如下:
class Base { public: virtual Base& clone(void); }
提供虚工厂函数。

四,类的设计和声明

4.1 类应是描述一组对象的集合

4.2 类成员应是私有的private

4.3 保持对象状态信息的持续有效

4.4 提高类内的聚合度

4.5 降低类间的耦合度

4.6 努力使类的接口小而完备

4.7 保持类的不同接口在实现上的一致性

4.8 保持不同类的接口在实现上的一致性

4.9 避免为每个类成员提供访问函数

4.10 不要在类定义时提供成员函数体

4.11 函数声明是定义参数的缺省值

4.12 恰当选择成员函数,全局函数,友元函数

4.13 防范,杜绝潜在的二义性

4.14 显式禁止编译器自动生成不需要的函数,通过声明为private,不给出定义。

4.15 保证,对象对每个错误都有应对措施

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

五,继承

5.1 公共继承表示派生类是基类

5.2 关于“有”和“由...实现”
包含意味着有一个或由...实现,私有继承表示由...实现
区别: 包含的两个类不能互相替换,而公共继承的派生类可以替换基类。
编译器不会将私有继承的派生类转换为基类。私有继承可以访问基类的protected成员,
包含不行。
私有继承可以重写基类的虚函数,包含不行。尽量使用包含。

5.3 关于继承和模板,当类型差异不影响类的行为时用模板,否则用继承

5.4 关于继承接口和继承实现
纯虚函数: 只继承接口,强制派生类实现接口
一般虚函数: 继承接口提供缺省实现,可修改
非虚函数: 继承接口和实现
私有继承:只继承实现

5.5 限制继承的层数,一般最大为5

5.6 继承树上非叶子节点的类应是虚基类

5.7 显式提供继承和访问修饰,public, protected, private

5.8 显式指出继承的虚函数

5.9 基类析构函数首选虚函数

5.10 绝不要重新定义非虚函数,考虑用虚函数

5.11 绝不要重新定义缺省参数值,因为缺省参数值是静态绑定的

5.12 不要将基类强制转换成派生类

5.13 关于c++中分支用法选择
若分支依赖于对象的值,用if else/switch, 若依赖于对象的类型,用虚函数。

5.14 慎用多重继承,防止菱形继承

5.15 所有多重继承的基类析构函数都应是虚函数

六,内存分配和释放

6.1 用new,delete取代malloc,calloc,realloc和free

6.2 new,delete和new[], delete[]要成对使用

6.3 确保所有new出来的东西适时被delete掉
广义上的内存泄漏,是指没有及时释放内存。而不单单指没有释放内存。

6.4 谁申请谁释放
引入了auto_ptr,自动指针的概念。

6.5 当对象消亡时确保指针成员指向的系统堆内存全部被释放

6.6 自定义类的new/delete操作符一定要符合原操作符的行为规范

6.7 自定义类的new操作符一定要自定义类的delete操作符

6.8 当所指的内存被释放后,指针应有一个合理的值,比如NULL

6.9 记住给字符串结束符申请空间,'/0'

七,初始化和清除

7.1 声明后就初始化强于使用前才初始化,因为前者只调用一次构造函数,后者还调用赋
值函数

7.2 初始化要彻底

7.3 确保每一个构造函数都实现完全的初始化

7.4 尽量使用初始化列表,在初始化列表上的成员,只调用一次构造,而在内部的多调用
一个赋值函数。

7.5 初始化列表要按成员声明顺序初始化, 因为初始化顺序是按照定义时的顺序来进行的


7.6 构造函数没结束,对象就没有构造出来。

7.7 不要用构造函数初始化静态成员,因为静态成员属于类,而不属于对象。

7.8 拷贝构造函数和赋值函数尽量用常量参数

7.9 让赋值函数返回当前对象的引用, return *this

7.10 在赋值函数中防范自己赋值自己

7.11 拷贝和赋值要确保彻底

7.12 关于构造函数,析构函数,赋值函数,相等或不等函数的格式
ClassA::ClassA(const ClassA&);
~ClassA(void);
ClassA& operator=(const ClassA& rhs);
bool operator==(const ClassA& lhs, const ClassA& rhs);
bool operator!=(const ClassA& lhs, const ClassA& rhs);

7.13 为大多数类提供缺省和拷贝构造函数,析构函数,赋值函数,相等函数

7.14 只有在有意义时,才提供缺省构造函数

7.15 包含资源管理的类应自定义拷贝构造函数,赋值函数和析构函数

7.16 拷贝构造函数,赋值函数和析构函数要么全自定义,要么全自动生成

7.17 类应有自己合理的拷贝原则:或浅拷贝或深拷贝

7.18 若编译时会完全初始化,不要给出数组的尺寸。
int array[4] = { 1, 2, 3, 4}; 改用int array[] = { 1, 2, 3, 4};

7.19 将循环索引的初值定在循环点附近

7.20 确保全局变量在使用前被初始化

八,常量

8.1 关于常量修饰符的含义
const char *p; 表示指向常量的指针
char * const p; 表示指向char的常量指针
const char* const p;表示指向常量的常量指针

8.2 在设计函数原型时,对那些不可能被修改的参数用const修饰

8.3 类成员可以转换成常量形式暴露出来

8.4 关于常量成员函数,声明应该以const结尾。

8.5 不要让常量成员函数修改程序的状态

8.6 不要将常量强制转换成非常量

8.7 任何变量和成员函数,首选用const修饰

九,重载
c++的重载功能使得同名函数可以有多种实现方法。

9.1 仔细区分带缺省值参数的函数和重载函数

9.2 确保重载函数的所有版本有共同的目的和相似的行为

9.3 避免重载在指针和整型类型上,以防止二义性

9.4 尽量避免重载在模板类型上

十,操作符
可自定义的操作符: operator new operator delete operator new[] operator
delete[]
+ - * / % += -= *= /= %=
& | ^ ~ &= |= ^= >> << >>= <<= ! = < > != == <= >= && || ++ -- -> ->*
, () []
不可自定义的操作符: . .* :: ?: new delete sizeof typeid
static_cast dynamic_cast const_cast reinterpret_cast

10.1 遵守操作符原本的含义,不要创新

10.2 确保自定义操作符能和其他操作符混合使用

10.3 区分作为成员函数和作为友元的操作符
+= =等需要左值,应是成员函数。而 == +不需要左值,应是友元
操作符作为成员函数可以确保左值一定是该类的对象,而作为友元,可以使第一个操作
数为任何类型。

10.4 关于前后缀操作符
定义时的分别: 前缀 ClassA& operator++(void); 后缀 ClassA const
operator++(int);
后操作符应返回常量,防止类似 i++++的用法。

10.5 确保相关的一组操作符行为统一

10.6 决不要自定义operator&&(), operator||()和operator,()

十一,类型转换

11.1 尽量避免强制类型转换

11.2 如果不得不作类型转换,尽量用显式方式

11.3 使用新型的类型转换并确保选择正确
const_cast:将常量或非常量转换成非常量或常量
dynamic_cast:将继承类转换成派生类或相反,运行时确定转换是否成功
static _cast:类似于c的强制类型转换,但不能同const_cast一样
reinterpret_cast: 可以转换成任意类型

11.4 用虚函数方式取代dynamic_cast

11.5 自定义类最好提供显式而不是隐式转换函数

11.6 使用关键字explicit防止单参数构造函数的类型转换功能

11.7 限制隐式类型转换的类型数

11.8 避免多个函数提供相同的类型转换

十二,友元

12.1 少用友元,除非,有些操作符函数,协作类之间的内部通信,比如容器和迭代器

12.2 减少拥有友元特权的个数

十三,模板

13.1 使用模板如果有限制条件一定要在注释和文档中描述清楚

13.2 模板类型应传引用/指针而不是值

13.3 注意模板编译的特殊性

13.4 嵌套template 的 >>中间要加空格以区别于operator >>
vector< < set > >

十四,表达式和控制流程

14.1 让表达式直观

14.2 避免在表达式中用赋值语句

14.3 不能将枚举类型进行运算后再赋给枚举变量

14.4 避免对浮点类型作等于或不等于判断

14.5 尝试用范围比较代替精确比较
for (i = 0; i != 10; ++i);
for (i = 0; i<10; ++i);

14.6 范围用包含下限不包含上限方式表示

14.7 关于goto
不能用goto跳出/跳入循环体,不能用goto跳出/跳入程序块

14.8 在循环过程中不要修改循环计数器

十五,宏

15.1 彻底用常量代替类似的宏

15.2 代码中的数值应由一个有意义的标识符代替

15.3 若宏值多于一项,一定要使用括号

15.4 不要用分号结束宏定义

15.5 彻底用inline函数替代类似的宏函数

15.6 不好被替代的宏函数,当不想指明参数类型时

15.7 函数宏的每个参数都要括起来

15.8 不带参数的宏函数也要定义成函数形式

15.9 用 { } 将函数宏的函数体括起来

15.10 彻底用typedef代替宏定义新类型

15.11 不要在公共头文件中定义宏,除非用于头文件的多次引用而定义的宏

15.12 不要用宏改写语言

十六,异常处理

16.1 确保代码在异常出现时能正确处理

16.2 正确注释代码的异常处理能力

16.3 减少不必要的异常处理

16.4 不要利用异常处理机制处理其他功能

16.5 注意模板类型可能会破坏异常处理的一些约定

16.6 确保异常发生后资源还能被回收

16.7 特别当心析构时发生异常
c++规定,异常处理期间,如果再发生异常,程序就终止,所以如果因为异常而调用到
了析构,那么再发生异常,程序一定会终止。

16.8 抛出的异常最好是一个对象

16.9 捕捉异常时,绝不要先基类后派生类

16.10 捕捉异常时,用引用

十七,代码格式

17.1 水平缩近每次用两个空格

17.2 不要在引用操作符前后加空格

17.3 不要在单目操作符和其操作对象间加空格

17.4 不要在::前后加空格

17.5 在, ; 之后加空格

17.6 在关键字和其后的(之间加一个空格

17.7 文件中的主要部分用空行分开

17.8 函数间要用空行分开

17.9 组局部变量声明和代码之间用空行分开

17.10 用空行将代码按逻辑片断划分

17.11 可以考虑将if块和else/else if 块用空行分开

17.12 函数返回语句要和其他语句用空行分开

17.13 每一行不超过78个字符

17.14 当一条语句超过78个字符时,按逻辑划分成不同行

17.15 花括号{ } 要单独占一行

17.16 花括号中没有或只有一条语句时也不省略花括号

17.17 不要在一行中放多于一条语句

17.18 语句switch中的每一个case各占一行

17.19 语句switch中的case按字母顺序排列

17.20 为所有switch语句提供default分支

17.21 若某个case不需要break一定要加注释声明

17.22 变量定义应集中放置,各占一行,并按字母顺序排列

17.23 定义指针和引用时 * 和 & 紧跟类型

17.24 按编译器解析顺序放置变量声明的修饰符
const char* pName; 最好写成 char const* pName

17.25 关于函数声明和定义的格式,声明原则上放在一行。定义可以按以下顺序放在多行
模板描述, 修饰符inline 和返回值类型, 函数名及其参数,函数体

17.26 函数名和左括号间不空格

17.27 声明函数时,给出参数的名字,除非没有用处

17.28 关于类内不同级别的元素排列顺序
类的公共部分(public), 被禁止使用的函数部分, 注释横线,类的保护部分,类的私
有成员函数部分,类的私有成员,最后是类的友元声明

17.29 关于类成员函数的排列顺序
先是构造函数和析构函数,然后是操作符函数和其他函数,按功能归类。

17.30 类成员变量按字母顺序排列

17.31 关于静态成员的访问,用操作符::,避免用.或->

17.32 关于字符常量,用'a'的形式,避免使用0x64

17.33 用带颜色的编辑器

十八,注释

18.1 用英语写全部的注释

18.2 确保注释完善你的代码,而不是重复你的代码

18.3 注释用词要精确,不能有二义性

18.4 注释中的术语要通用

18.5 注释要简单,清楚,切中要害

18.6 注释不能超出被注释代码所包含的内容

18.7 注释中避免引用容易变化的信息

18.8 确保所有注释及时更新

18.9 注释不具备约束使用者行为的能力

18.10 注释不要嵌套

18.11 不要用/* */注释掉大块代码,应该用#if 0

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

18.13 行末注释尽量对齐

18.14 单独的注释行和被注释语句缩近相同的空格

18.15 减少不必要的单独占一行的注释

18.16 对每个#else或#endif给出行末注释

18.17 对每个引用的头文件给出行末注释

18.18 对每个空循环体给出确认性注释

18.19 关于函数注释,使用方法的注释,实现的注释

18.20 关于注释频率 大约5行一句注释

十九,文件和目录

19.1 使用统一而且通用的文件名后缀

19.2 关于文件名的选择,清晰

19.3 关于文件/目录名的字符集选择,尽量用[A-Za-z0-9._-]

19.4 关于每个类的文件组成,类接口,实现,inline函数

19.5 关于模板类的文件安排,尽量放在头文件中实现。

19.6 保持文件前言的简洁性

19.7 关于文件的段落安排,前言,防止重复引用设置,#include部分,#define 部分,常
量声明,类型声明和定义,全局变量声明,文件级变量,文件级函数,函数实现,结束注


19.8 关于目录组织,按逻辑功能划分,公共头文件目录

二十,头文件

20.1 头文件多次引用的防范,头文件头加上#ifndef ***#define ... #endif 标记

20.2 确保公共头文件的自足性,包含自己所需的所有头文件

20.3 只引用需要的头文件

20.4 #include引用时" " 和 < > 的用法
< > 只用在引用系统头文件,其他用""

20.5 引用头文件的顺序,系统头文件,自定义头文件

20.6 引用时不要用绝对路径

20.7 将函数库房在一个单独的目录下引用

20.8 不要在头文件中定义常量/变量

20.9 任何声明若被多个源文件引用则应在一个头文件中

20.10 在源文件中不要用关键字extern

二十一,条件编译

21.1 最小化条件编译的使用范围

21.2 若使用#if 或 #ifdef , 不要遗漏#else

21.3 编译条件的含义要具体

21.4 对复杂的编译条件用括号使其清晰

21.5 条件编译和普通条件语句不要混合使用

21.6 若只测试某符号是否存在,不要给该符号赋值

二十二,编译

22.1 关注编译时的警告错误

22.2 把问题尽量暴露在编译时而不是运行时

22.3 减少文件的依赖程度

22.4 减少编译时间

22.5 透彻研究编译器

二十三,兼容性

23.1 遵守ANSI C和ISO C++国际标准

23.2 将不符合国际标准的代码与其他代码分开

23.3 不要假设字符类型是否是有符号类型

23.4 运算时显式转换有符号和无符号类型

23.5 注意双字节字符的兼容性

23.6 恰当使用位操作符

23.7 注意位域变量的兼容性问题

23.8 不要强制引用/指针指向尺寸不同的目标

23.9 确保类型转换不会丢失信息

23.10 不要假设类型的存储尺寸

23.11 如果一定要规定类型的存储尺寸,自行定义一套类型

23.12 不要假设对象的存储结构

23.13 注意运算溢出问题

23.14 不要假设表达式的运算顺序

23.15 不要假设函数参数的计算顺序

23.16 不要假设不同源文件中静态或全局变量的初始化顺序

23.17 不要依赖编译器基于实现,未明确或未定义的功能

23.18 注意数据文件的兼容性

23.19 注意引用公共库的兼容性

23.20 一定不要重新实现标准库函数

23.21 将所有#include 的文件名视为大小写敏感

23.22 代码中用到的路径只用 / 而不要用 /

23.23 确保main 函数总有返回一个整型

23.24 不要依赖pragmas

二十四,性能

24.1 使用性能追踪分析工具

24.2 不要用移位代替乘除运算

24.3 如无必要,不要用非int 的整型类型

24.4 不要使用关键字register

24.5 避免在循环体内部定义对象

24.6 减少代价很高的对象拷贝

24.7 减少临时对象

24.8 注意大尺寸对象数组

24.9 返回对象的优化

24.10 前缀++和--的效率更高

24.11 恰当使用递归

24.12 恰当使用inline函数

24.13 虚函数和虚继承效率会有一点损失

24.14 如果合理,使用编译器生成的函数

24.15 如果合理,构造直传类,既可以以字节copy的对象为直传类,有自定义copy函数的
为非直传类

24.16 关于缓存 类成员

24.17 关于标准库的性能

24.18 关于偷懒

24.19 关于勤快

24.20 80-20原则

二十五,其他

25.1 避免产生全局数据对象

25.2 确保任何定义只发生一次

25.3 指针操作中用NULL代替0

25.4 不要把NULL用在指针以外的地方

25.5 不要弄巧成拙,不要使用花哨的方法。

25.6 将不要使用的代码删除

25.7 运行时不要改变进程的环境变量

25.8 该用volatile时一定要用
volatile的含义是:其所修饰的变量可能会发生改变,而这种改变是编译器觉察不到的
。编译器就不会优化掉这个变量。

25.9 不要使用鲜为人知的替换符号

25.10 关于代码审查规范

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)