高质量c/c++编程...
时间:2010-08-18 来源:shandaliuyan
第1章
为防止头文件被重复引用,应当用 ifndef/define/endif 结构产生预处理块
使用 #include<filename.h> 引用标准库头文件,使用 #include"filename.h" 引用非标准库头文件
头文件中只存放“声明”,不存放“定义”
第2章
应在关联性不强的语句间添加空行
一行代码只做一件事情
if、for、while、do 等语句自占一行,且必须带上 {}
尽可能在定义变量的同时初始化该变量
关键字后面要添加空格,函数明后不留空格
for (int i = 0; i < 10; ++ i)
int sum(int i)
修饰符 * 和 & 应紧靠变量名
第3章
程序中不要出现仅靠大小写区分的相似的标识符,不要出现标识符相同的局部变量和全局变量
类名和函数名用大写字母开头的单词组合,变量和参数用小写字母开头的单词组合,常量全用大写字母,并用下划线分割单词
静态变量前加 s_,全局变量前加 g_,类的数据成员加 m_
第4章
为防止产生歧义并提高可读性,应当使用括号来确定表达式的操作顺序
与零值比较时,布尔变量使用 if (flag)、整型变量使用 if (1 == num)、浮点型变量使用 if (x >= -eps && x <= eps)、指针变量使用 if (NULL == p)
在多重循环中,如果有可能,应当最长的循环放在最内层,已减少CPU跨切循环的次数
不可在for循环体内修改循环变量,防止 for 循环失去控制
不要遗漏了 switch 语句中的每个 case 语句后面的 break,不要遗漏 default 语句
第5章
用 const 常量完全取代宏常量
如果某一常量与其他常量密切相关,应在定义中包含这种关系
const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;
类中的常量应使用枚举常量来实现,但要注意 enum 只能代表整数
第6章
函数的参数名要恰当,顺序要合理
如果函数参数是指针,且仅作输入用,应在类型前加上 const 以防止被无意修改
如果输入参数以值传递方式传递对象,则应用"const &"方式以省去临时对象的构造和析构过程
若函数没有参数,用void填充
若函数没有返回值,应声明为 bool 类型,并在函数体结尾 return true;
正常值用输出参数获得,而错误标志用 return 语句返回
在函数体的“入口处”,在必要时候要使用断言 assert 对参数的有效性进行检查
在函数体的“出口处”,要对 return 语句的正确性和效率进行检查,不可返回指向“栈内存”的指针或引用
函数功能要单一,不可带有“记忆”功能
函数的参数该用指针还是引用,要看函数对此参数的具体需求,“用适当的工具做恰如其分的工作”
第7章
内存分配的方式:
1.静态存储区域分配,全局变量,static变量
2.栈,局部变量
3.堆,使用 malloc 或 new 申请的内存
使用了 malloc 或 new 后应该立即检查指针是否为 NULL
不要忘记为数组和动态内存赋初值
避免数组或指针的下标越界,特别当心“多1”或“少1”的情况
动态内存的申请和释放必须配对,防止内存泄露
使用 free 或 delete 释放内存后,应立即将指针置为 NULL,防止产生迷途指针
C/C++语言无法知道指针所指的内存容量,必须在申请内存时记住它
当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针
不能使用指针参数来申请内存,只能使用“指向指针的指针”参数申请内存,可以使用返回指针类型的函数申请内存
- char *GetString(void)
- {
- char *p = "hello world"; //错误,"hello world"存储在静态存储区
- //char p[] = "hello world"; //错误,return 返回指向“栈内存”指针
- return p
- }
char *GetString(void) { char *p = "hello world"; //错误,"hello world"存储在静态存储区 //char p[] = "hello world"; //错误,return 返回指向“栈内存”指针 return p }
malloc/free 是标准库函数,new/delete 是运算符,malloc/free 无法动态创建自定义类类型对象,保留 malloc/free 主要是为了和 C 兼容
内存耗尽时(无法申请到内存),应马上使用 exit(1) 终止程序
若指针 p 非空,连续对 p 使用两次 free 会导致程序崩溃
用 delete 释放对象数组时要加上 []
第8章
当心隐式类型转换导致重载函数产生二义性
如果派生类的函数与基类的函数同名,但参数不同,此时,无论有无 virtual 关键字,基类的函数都将被隐藏
如果派生类的函数与基类的函数同名,且参数相同,但基类函数没有 virtual 关键字,基类的函数被隐藏
参数的缺省值只能出现在函数的声明中,而不能出现在函数的定义体中,不合理地使用参数缺省值将可能导致重载函数的二义性
inline不应该出现在内联函数的声明中,但必须出现在内联函数的定义中,应使用内联函数取代宏
第9章
对于任意的类,系统将自动为其产生构造函数、拷贝构造函数、析构函数和赋值函数,这四个函数最好自己定义,例如A a = b,a会自动调用拷贝构造函数,若A的构造函数中存在动态开辟内存的操作,则开辟内存的指针会在拷贝的时候复制,可是仅仅复制了指针的地址,但没有再次动态地开辟一次内存,这样a和b都存在一个指向同一空间的指针,当他们调用析构函数的时候,该内存空间就会被释放两次而产生错误,这种没有把内存资源拷贝多一份的现象称为“浅拷贝”
const常量只能在构造函数的初始化列表里被初始化
成员在初始化列表中的初始化顺序只由成员对象在类中声明的次序决定
拷贝构造函数可以调用本类的私有成员
- String::String(const String &other)
- {
- int len = strlen(other.m_data);
- m_data = new char[len + 1];
- strcpy(m_data, other.m_data);
- }
String::String(const String &other) { int len = strlen(other.m_data); m_data = new char[len + 1]; strcpy(m_data, other.m_data); }
赋值函数
- String & String::operate =(const String &other)
- {
- if (this == &other)
- return *this;
- delete [] m_data;
- int len = strlen(other.m_data);
- m_data = new char[len + 1];
- strcpy(m_data, other.m_data);
- return *this;
- }
String & String::operate =(const String &other) { if (this == &other) return *this; delete [] m_data; int len = strlen(other.m_data); m_data = new char[len + 1]; strcpy(m_data, other.m_data); return *this; }
为了防止内存泄露,析构函数必须为虚函数
派生类的构造函数必须在其初始化列表中调用基类的构造函数
编写派生类的赋值函数时,要记得对基类的数据成员重新赋值
第10章
只有在逻辑上B是A的“一种”,且A的所有功能和属性对B都有意义,才允许B继承A的功能和属性
第11章
多使用 const 提高函数的健壮性
小心数据类型发生隐式转换
避免编写技巧性很高的代码
不要设计面面俱到、非常灵活的数据结构
尽量使用标准库,不要“发明”已经存在的库函数
尽量不要使用与具体硬件或软件环境关系密切的变量