C++编码规范

近期,在小组内做了一期关于编码规范的小培训,将编写的编码规范文件分享与下文。组内成员有不同的背景和风格,因此几个工程的编码风格迥异,不乏有一些不规范,有风险的地方。目前网上流行的编码规范一般大而全,考虑到不影响现有工作进行,循序渐进的改进方法,我尽量选择了重要的条目,并且进行了分级。结合组内实际情况,突出了一些常见问题。

术语:

原则:编程时要遵循的指导思想

建议:处于代码可读性,性能方面给出的建议。

规则:编程中应该执行的编码规则。

强制:编程中必须执行的编码规则。

禁止:一旦触犯,会引发严重错误的。

背景:为说明情况补充的背景知识。

编码原则

1.【原则】清晰第一:一般情况下,代码的可阅读性高于性能。

2.【原则】简洁为美:简洁就是利于理解并且利于实现。

3.【原则】选择合适的风格,与代码原有风格一致。如果重构代码时,明智的作法是依据现有风格继续编码。

日志

1.【背景】日志的作用:1)定位问题 2)性能分析。

2.【强制】重要的处理环节 一定要有启动,结束日志。(对程序崩溃定位非常有效)

3.【建议】避免在大循环中写无关紧要的日志。

注释

1.【原则】尽量让代码拥有自解释能力。

2.【原则】在代码的功能、意图层次上进行注释,即注释解释代码难以直接表达的意图,而不是重复描述代码。

3.【规则】修改代码时,维护代码周边的所有注释,以保证注释与代码的一致性。不再有用的注释要删除。

4.【强制】public函数一定要有注释,说明功能及用法,输入,输出,返回值的意义。(其实,这也可以帮助你更加注意访问权限控制,如非必要,你肯定不会“默认”采用public关键字的)

5.【强制】关键算法模块一定要有注释,尤其在参数值、特定情况存在的情况下。(如果算法是针对某种特殊情况进行处理的,那么说明这个情况是非常有必要说明的)

头文件

1.【建议】头文件中适合放置接口的声明,不适合放置实现。(即使是模板类,也会在头文件中include .hpp文件的方式保证头文件只有声明。如果是非模板类,你可能会获得方法已经定义的错误)

2.【建议】头文件应当职责单一。(“单一”的粒度应该比较小,而非单一的做一件非常大的事情)

3.【规则】每一个.cpp文件应有一个同名.h文件,用于声明

4.【强制】.cpp/.h文件禁止包含用不到的头文件。(这种不仅会带来额外的编译预处理时间,而且也可能给引用者带来不小的麻烦)

5.【规则】头文件应当自包含(自给自足)。

6.【强制】禁止在头文件中定义变量。(static变量除外,因为static变量不会引发重定义问题)

7.【规则】使用前置声明(forward declarations)尽量减少.h文件中#include的数量。(扩展:哪些情况可以使用前置声明?

  1. 数据类型为指针、引用类型时
  2. 数据类型为函数的参数、返回值类型时
  3. 数据类型为静态成员类型时,因为静态成员类型的定义在类外。)

8.【规则】因为有模板存在必须使用hpp的文件,已存在的采用声明和定义分开的方式。新建的采用 .h声明,最后#include .hpp的方式。

作用域

1.【强制】禁止在头文件或者#include 之前使用 using namespace xxx。(这样等于“自废武功”)

函数

1.【规则】一个函数仅完成一件功能。(这样保证了函数被使用的范围尽量广)

2.【规则】重复代码应该尽可能提炼成函数。

3.【规则】避免函数过长,函数不超过200行(不含注释行,空行)。(网上广为流传的华为公司的规范为不超过50行。但华为规范同时说业内流行的标准为不超过一屏)

4.【规则】避免函数的代码块嵌套过深,函数的代码块嵌套不超过4层。(我们在阅读代码时,也像执行代码一样保存了“栈”,过深的嵌套超出了我们的脑力……)

5.【规则】废弃代码(没有被调用的函数和变量)要及时清除。

6.【建议】函数的参数个数不超过5个(避免混淆)

7.【强制】对传入参数进行合法性检查。(我们很多程序的崩溃均是这个原因构成的)

8.【强制】对非传入参数进行合法性检查。

9.【建议】对文件内部函数使用 static关键字(static用来表示函数的作用域为文件内)

10.【建议】对于短小的并且在大循环中调用的函数可以采用inline方式。(inline即用函数保证了简洁性,而且也保证了效率。其实,编译器会自行决定哪些函数被内联)

函数命名

1.【规则】名称第一个单词必须是动词或者动名词。

对象

1.【规则】尽可能使用const.函数传参尽量使用const 引用代替传值。但对于内置类型,STL迭代器,函数对象,传值的效率更高一些。(const 函数的内部仅允许使用const类型的函数(本类),这样会形成const调用链……这正是其魅力所在)

2.【强制】确保对象被使用前已被初始化。(定义与函数体内部的内置类型(即POD,plain old data类型)将不会被初始化)

变量

1.【建议】不用或者少用全局变量。

2.【建议】结构功能单一;不要设计面面俱到的数据结构。 (这样不能起到结构复用的目的,而且使得数据结构的意图含糊不清)

3.【建议】尽可能局部的声明变量。

【背景】C99以前的c语言版本曾经要求只能在作用域开始处定义变量,因此有的程序会在函数开始处定义所有使用的变量。大家现在没必要遵循这种写法。

变量命名

1.【建议】不建议使用匈牙利命名法。(因为匈牙利命名法将类型写在命名中的做法会给类型修改时带来不便)

2.【规则】标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。

3.【强制】除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音

4.【强制】禁止使用单字节命名变量,但允许定义i、j、k作为局部循环变量。

5.【建议】类的成员变量尽量以“m_”开始

1.【建议】常量建议使用const定义代替宏。

2.【建议】除非必要,应尽可能使用函数代替宏。(用宏时,很可能带来意外的效果)

3.【强制】避免使用“魔数”。(尤其是在算法类的程序中,使用参数时直接写数字相较采用变量定义参数,然后传入难理解的多)

内存管理

1.【禁止】禁止内存操作越界。尤其在访问数组时,不得在没有判断数组是否为空的情况下访问首元素。在数组元素个数可能发生改变的循环中,需要慎重考虑下标/迭代器的有效性。

2.【禁止】禁止内存泄露。在堆上申请的对象要适时释放。

3.【禁止】禁止引用已经释放的内存空间。释放对象时慎重考虑其他可能保留引用的对象。

程序优化

1.【原则】在保证程序正确性、简洁、可维护、可测的情况下,优化代码效率。

2.【原则】优先考虑对数据结构和算法的优化来改进程序效率。

3.【原则】先实现正确的程序,后考虑优化。不要二者同时进行。(在参考文献5中第一句话就是“以效率优化为名(但未实现)所犯的错误比其他任何错误都多”)

参考资料:

  1. 《华为技术有限公司c语言编程规范》
  2. 《google的C++编码规范》
  3. 《C++编程规范101条规则、准则最佳实践》
  4. 《Effective c++》
  5. C++ Optimization Strategies and Techniques(关于c++优化的延伸阅读)

 

 

发表评论

电子邮件地址不会被公开。