C++程序checklist

转载请注明出处:http://blog.csdn.net/wangxiaolong_china


检查范围

检查项

检查原因

典型事例

命名规范

有意义的变量或常量命名符合习惯吗?

提高可读性

比如程序中约定的所有端口定义为port,所有通道定义为path

多个数据命名时容易引起混淆吗?

提高可读性

程序中一会使用clk表示时钟,一会又使用timer来表示时钟

变量的类型定义合适吗?

避免数据范围导致的bug

有符号和无符号,INT还是long,需要保证数据的类型定义满足逻辑表示的需要

变量名重复么

避免变量的错误使用导致bug

不要在一个函数实现中定义两个同名但作用域不同的变量

数组使用

不要使用立即数定义数组大小

避免数组范围和访问bug

int intarray[13];
推荐: int intarray[TOT_MONTHS+1];
解释: 使用立即数定义数组大小,会导致程序易读性较差,且因为与控制该数组的循环边界代码绑定过于紧密,导致今后代码可扩展性变差.

数组大小定义是否足够

避免数组越界范围的bug

char entry[TOTAL_ENTRIES];
推荐: entry[LAST_ENTRY+1];
解释: 总是推荐定义数组时采取实际需要大小加1的方式,这样无论时安全性还是程序的可读性方面都会提升 不少,同时尽量避免了数组访问代码的一些潜在的错误.

数组访问越界了吗

避免数组越界范围的bug

任何情况下都应该保证数组下标不会超过数组的最大范围,这个是及其容易导致的错误. 使用复杂变化的变量或其他外部传入的变量来做数组下标而不加检查的话,程序很难保证数组下标的合理性

常量

应该定义成常量的定义成了变量

提高效率,编码数据被修改

int months_in_year = 12;
推荐: const unsigned months_in_year = 12;
解释: 对于从来就不会变化的变量定义,应该使用常量定义,这样做有几个好处,不占用额外的内存空间,运行时因为不用寻址而执行效率更高..

可以定义成常量的地方不要使用#define定义

提高代码纠错能力

缺陷: #define MAX_FILES 20
推荐: const unsigned MAX_FILES = 20;
解释: 使用常量定义代替编译宏定义可以获取到很多好处,至少可以保证更加强大的编译器错误检查带来的好处

只在某个类中实现时用到的常量定义是否定义成了全局的

提高代码内聚和封装性

缺陷:
const unsigned MAX_FOOS = 1000;
const unsigned MAX_FOO_BUFFERS = 40;
推荐:
class foo {
public:
enum { MAX_INSTANCES = 1000; }
...
private:
enum { MAX_FOO_BUFFERS = 40; }
...
};
将不该给外部使用的定义隐藏起来,可以保证类封装的完整性,并给后续类代码重构带来好处.对于超过int的常量定义

变量使用

程序使用float 或 double恰当吗

提高代码效率

缺陷: double acct_balance;
推荐: unsigned long acct_balance;
解释: 通常情况下仅仅在科学计算或者导航应用方面需要用到浮点数,相对整数而言,浮点数运算不仅耗时,而且在溢出处理方面也比较复杂.例子中的财务计算,单位表示到”分”,这样acct_balance可能等于103446,打印输出为$1,034.46.显然没必要定义成double.

变量是否初始化了


在任何情况下,变量都需要初始化

变量范围是否足够表示数据

避免数据范围导致的bug


类使用

有虚函数的类的析构函数定义成了虚函数了吗

避免内存泄漏

存在虚函数的意义在于允许类继承,而类继承时,为了让隐私类销毁时正确调用到该派生类析构函数,必须将基类析构定义成虚函数.

拷贝构造,运算符重载,析构实现完整了吗

提高类完整性

通常情况下,拷贝构造,运算符重载和析构应该三者同时实现,唯一例外是只有析构没有其它两个实现.

是否正确避免了没实现赋值重载操作符的类被错误的调用了系统缺省赋值操作

保证类定义完整性

在没有实现某个操作符重载函数时,类是否避免了外部调用系统提供的缺省的赋值操作

类的成员变量是否已经尽可能的封装到类内部了

提高封装性

不要将可以private的类成员声明成了public,这样会导致类封装变差

每一个派生类中都需要的成员变量应该定义在基类中

提高复用性

这样可以最大发挥类继承的效果.便于在基类中提供统一的处理和相关实现

类继承层次单一

降低复杂性,提高可读性

不要设计非单一层次的类继承.复杂的继承关系会导致实现难于控制,除非是纯虚接口

构造函数中分配系统资源了么(内存,互斥量等)

降低出错概率

不推荐在类构造函数中申请内存.原因是目前系统不处理异常,而构造时申请内存如果不成功,会直接因为系统无法处理操作系统抛出的异常而导致程序崩溃

字符串使用

字符串是以null结尾的吗?

避免字符串越界bug

任何情况下,都应该保证字符串以null作为正确结尾,否则会产生一项不到的后果

是否使用std::string

防止字符串操作出错

在资源允许的情况下,尽量使用std::string,除非是效率要求高,特殊算法,在内核驱动等情况下

宏定义

如果宏参数在宏定义中出现多次,要避免宏展开时导致的逻辑错误?

避免宏使用出错

缺陷:
#define max(a,b) ( (a) > (b) ? (a) : (b) )
max(i++, j);
推荐
inline int max(int a, int b) {return ((a) > (b) ? a : b );}
max(i++, j);

宏定义的运算结果加了括号吗

避免宏使用出错

任何情况下,如果宏中存在运算,将运算结果定义加上括号,可以避免在宏展开时导致一些异常情况发生

宏定义中,参数加了括号了吗?

避免宏使用出错

#define IsXBitSet(var) (var && bitmask)
result = IsXBitSet( i || j );
展开后为: result = (i || j && bitmask); // not what expected!
推荐
#define IsXBitSet(var) ((var) && (bitmask)) 任何情况下,将宏定义中的输入参数加上括号,可以避免在宏展开时导致一些异常情况发生.

内存分配和释放

代码分配内存但是假定了在别处释放吗

避免内存使用错误

在一处分配内存,希望再其它地方释放,这种做法一般不会有问题,但是容易导致错误.至少应该做到如下几点:详细设计文档或相关代码中一定要明确写明;除非将其封装为一个类,在构造时申请并在析构时释放掉内存.如果内存申请后可能多次使用,但不能确切知道最后一次使用,可以考虑使用引用计数的方式

程序应当尽量使用new而不是使用相关C函数来分配内存?


malloc(), calloc(), or realloc()都是C函数,一般情况下C++程序应该尽量使用new而不是这些C函数来分配内存

数组释放正确吗


缺陷: delete myCharArray;
推荐delete [] myCharArray;
解释:务必注意正确的使用delete释放数组的方式

在多处使用的内存指针尽量使用share_ptr模板



释放后指针是否设为空


避免重复释放

释放内存前是否判断指针为空


避免重复释放

运算

尽量不要使用运算符的缺省优先级而使用括号来消除疑惑?

避免优先级出错

缺陷: if ( a = function() == 0 )
推荐if ( (a = function()) == 0 )
解释:很多人对自己脑海中记住的运算符非常自信,所以是宁愿少使用括号.这是一个危险的想法.因为运算符如此多的情况下,你有时犯晕也是有可能的,但是这种因为优先级而导致的问题往往隐藏得很深.

你想要=还是==?

防止错误输入

缺陷: if ( a = function())
推荐if ( a == function())
解释:抛开程序运用的环境,上面两种情况都有可能,但是你究竟是要写=还是==,不要在这个地方犯错误.

是否有些需要同步的数据并没有被更新?

保证正确性

有时,多个数据形成一组数据,这一组数据中具有紧密的相关性,程序应该保证在刷新其中一个数据时保证其它相关数据的同步刷新.否则任何中间状态的组合取值都可能因为无意义而导致程序逻辑运算异常

数据在运算是是否发生了上溢或下溢错误?


当运算发生了溢出错误时,往往时定义的数据类型过小导致,多数情况下不会发生溢出,但需要仔细计算和对运算数据取值范围做精确评估

不要试图精确判断浮点数大小


if ( someVar == 0.1 ) someVar为浮点数时可能永远都得不到正确的值.解决方法是使用>,>=,<=这类判断来确定浮点数的值.

不要试图去判断一个无符号数>=0


缺陷: if ( myUnsignedVar >= 0 )
解释:这个条件判断永远都是true,已经失去判断的意义了.

使用有符号数做计数器时,对边界值判断最好不要使用==


缺陷: if ( mySignedVar )
推荐if ( mySignedVar>=0) 或者if ( mySignedVar>=0)
解释:计数器变化时,精确判断为0可能导致程序错过该边界值而引入一些严重的错误.因此对有符号计数器边界值判断,建议不要使用精确判断

在两个不同类型的变量之间做比较吗?


尽量不在两个不同类型的变量之间做比较.

不要混用了|和||?


这两个运算符功能完全不同,只是看起来有些象而已

不要混用了&和&&


这两个运算符功能完全不同,只是看起来有些象而已

switch

每个switch的case分支都有break结尾吗?


一般情况下,每个switch的case分支都应该有break结尾,如果没有,很可能执行结果并不是期望的执行逻辑.如果的确期望这样的结果,建议合并这几个case而不是写出这种缺少break的case分支.如果的确需要这样做,详细的代码注释加以解释是非常必要的

Switch的某个case以return结束时注意释放相关资源了吗?


一般情况下,case的结束应该使用break,这样在switch处理结束后可能需要对相关资源的释放操作才会得到正确的执行.如果以return结束case分支,需要关注是否正确的释放了相关资源(互斥量,内存等).

Switch缺少default处理吗


即使你100%肯定不需要default,也请提供一个default函数,这样做至少给程序的可扩展性带来好处

循环

循环判断条件是否超过了循环变量的表示范围


例如for (Octet loop=0; loop < 500; loop++),显然是一个死循环

正确选择了3种循环体了吗?


for /do while/while三种循环体适用的地方有一些习惯性的差别,尽管有时它们显得可以相互代替

循环体处理中正确设置了循环终止标志了吗?


如果循环体中没有任何操作改变循环推出标志,则会导致死循环的发生.这中情况有可能出现在循环体中的一些错误的条件判断下对循环退出标志进行设置.

尽量避免在循环体中的if处理分支中使用continue


在循环体中的if处理分支中如果需要使用continue,建议使用else代替

循环体退出出现多个出口时,每一个出口都是必须的并且保证了其正确性吗


循环体退出如果有多个出口,需要仔细考虑其必要性和退出循环的逻辑正确性

要避免过深的循环嵌套


过深的循环嵌套会导致程序执行效率低下,且容易产生错误

尽量能够使用std和boost标准库中循环方式


减少错误概率,提高可读性

函数/方法

局部变量是否包含了数组或者大的数据结构


过大的局部变量(数组,结构,类)都可能造成堆栈溢出,因此原则上大于512个字节的局部变量都最好使用动态分配内存的方法解决

函数命名符合规范吗?


如Get,Query,Set等函数前缀都有其约定用法,如果一个函数在实现Get的同时还完成了其它功能,那么它不应当仅仅取名为GetXXX..

函数实现时对传入参数做了正确检查了吗?


原则上应该对所有传入函数的参数都做合理性检查,至少应该使用断言进行检查.

函数所有出口都能够正确返回结果吗


当一个函数题出口存在多个时,程序是否保证了每一个出口都返回了正确的函数执行结果..

使用缺省参数的方式来代替函数重载吗?


通常情况下不建议使用缺省参数的方式来代替函数重载.这样会给程序的可读性和可扩展性带来不良影响.

是否尽量避免了函数和操作符重载?


通常情况下不建议使用函数和操作符重载,这同样会给程序的可读性和可扩展性带来不良影响.

注释

每一个函数,类,文件在其头上都有一个符合规范的注释吗?


规范化的函数,类,文件头注释,不仅可以自动生成详细设计文档,而且也可以引导编程者写出完备的注释.

每一个变量和常量声明处都有注释吗?


应该尽量载变量和常量的声明处加上注释,这样可以让程序变得容易维护

代码实现和注释保持了一致性吗


每一个函数或类的注释头与具体实现保持了一致吗?在进行代码修改时,人们往往忘记了同步修改相关注释,导致代码的可读性变差

代码中的注释是否有意义?


在函数头的注释中把所有的输入参数罗列一遍,却不解释每个参数的含义


你可能感兴趣的:(C++程序checklist)