C++ 编程风格建议
安全
引用和指针
- 引用不会为空
- 引用不会改变指向
- 引用不能进行+、-、++、–运算
宏定义多条语句
- 使用do…while(false)来包含
逻辑符号||的执行
假设我们要同时自增a和b,如果任意一个函数失败了,则执行某些操作
Bad
if(!IncreaseA() || !IncreaseB())
假设自增A函数失败了,则不会自增B
Good
分开写尽可能缩短变量的存活时间
- 短的变量存活时间减少了初始化错误的可能
- 阅读者同一时间需要阅读的代码变少,便于理解
- 当需要把一个大的函数分拆,短的存活时间方便拆分
避免浮点数的数量级相差巨大的数字之间的四则运算
double d = 100000000.0 + 0.1;避免浮点数的等量比较
if/循环语句必须使用大括号
禁止通过声明的方式引用外部函数接口、变量
通过extern声明的方式使用外部函数接口、变量,容易在外部接口改变时可能导致声明和定义不一致。类的成员变量必须显式初始化
基类的析构函数必须声明为virtual
禁止虚函数使用缺省参数值
禁止重新定义继承而来的非虚函数
不允许使用宏来表示常量
- 宏是简单的文本替换,在预处理阶段时完成,运行报错时直接报相应的值
- 跟踪调试时也是显示值,而不是宏名
- 宏没有类型检查,不安全
- 宏没有作用域
禁止用memcpy_s、memset_s初始化非POD对象
- POD类型主要包括int, char, float, double, enumeration, void, pointer等原始类ing以及聚合类型,不能使用封装和面向对象特性(如用户定义的构造/赋值/析构函数、基类、虚函数)
- 由于非POD类型比如非聚合类型的class对象,可能存在虚函数,内存布局不确定,跟编译器有关,滥用内存拷贝可能会导致严重的问题。
- 即使对聚合类型的class,使用直接的内存拷贝和比较,破坏了信息隐蔽和数据保护的作用,也不提倡使用memcpy_s、memset_s
含有变量自增或自减运算的表达式中禁止再次引用该变量
x = b[i] + i++;不要保存std::string的c_str()返回的指针
对于指针和引用类型的形参,如果是不需要修改的,请使用const
使用强类型参数,避免使用void*
使用std::string代替char*
- 不用考虑结尾的’\0’
- 可以直接使用+, =, ==等运算符以及其他字符串操作函数
- 不需要考虑内存分配操作,避免了显式的new、delete, 以及由此导致的错误
效率
循环嵌套, 把大循环写在里面
strcmp的判断
Bad
if(!strcmp(str1, str2))
Good
if(strcmp(str1, str2) == 0)肯定语句比双重否定容易理解
Good
if(SomethingDone)
Bad
if(!NotDone)条件判断语句
常量在右,变量在左
让编译器去检查误赋值的情况使用小括号,避免优先级问题
Bad
if(a < b == c == d)
Good
if((a < b) == (c == d))为变量指定唯一用途。避免采用不同取值区间来区分不同内容, 如,Account小于5000时表示老用户ID,大于5000时表示新用户ID
避免采用硬编码
布尔变量的命名
- 避免采用status、sourcefile等模糊的布尔变量名,采用statusOK、sourcefileFound
- 避免采用否定形式的布尔变量
if(!NotSuccess)
避免在变量名中使用数字
- 考虑使用数组代替
- 如果数组不适合,那么数字更不适合
定义变量的作用域
- 开始采用最严格的可见性,然后根据需求扩展变量的作用域
- 循环内的变量挪动到循环外,比反过来简单
- 把private变量变为public, 比反过来简单
不用的代码段直接删除,不要注释掉
- 被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入容易被忽略的缺陷
- 使用版本控制来,记录代码。
避免在变量名中使用容易混淆的字符
- 数字
1
和小写的l
- 数字
1
和大写的L
- 数字
0
和大写的O
- 数字
2
和小写的z
- 数字
6
和大写的G
- 数字
为空语句创建一个DoNothing()预处理宏或者内联函数
避免函数过长,函数不超过50行(非空非注释)
- 函数应该可以一屏显示完(50行以内), 只做一件事情,而且把它做好。
- 过长的函数往往意味着函数功能不单一,过于复杂,或过分呈现细节,未进行进一步抽象。
- 即使一个长函数现在工作的很好,一旦有人对其修改,有可能出现新的问题,甚至导致难以发现的BUG。建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。