C++ 编程风格建议
本文总结了C++编程中的安全与效率优化建议。安全方面,强调使用引用而非指针、避免宏定义、缩短变量存活时间、禁止浮点数等量比较、类成员变量显式初始化、虚函数禁用缺省参数等。效率方面,建议优化循环嵌套、避免硬编码、使用强类型参数、避免布尔变量否定形式、删除无用代码等。此外,提倡使用std::string
代替char*
、避免void*
、减少函数长度等,以提高代码可读性和维护性。这些建议旨在帮助开发者编写更安全、高效的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。建议将其拆分为更加简短并易于管理的若干函数,以便于他人阅读和修改代码。