狎昵关系
18. 异曲同工的类
19. 不完善的程序库类
20. 纯稚的数据类
21. 被拒绝的遗赠
22. 过多的注释
具体内容请参考《重构 改善既有代码的设计》, 在写代码的过程中,加红的几个是特别需要注意的
编译设置
在高警告级别进行编译,使用VC编译器应将警告级别打到最高4级,并设置将警告作为错误
宁要编译时和连接时错误,而不要运行时错误
项目设置include, lib路径使用相对路径,不要使用绝对路径
debug编译生成的是名称后加d ,如(debug是xxxd.exe, release是xxx.exe)
头文件
正确使用头文件可令代码在可读性、文件大小和性能上大为改观。
头文件以.h作扩展名
#define保护
所有头文件 都应该使用#define防止头文件被多重包含(multiple inclusion),命名格式为:
_
_
_H_
为保证唯一性,头文件的命名应基于其所在项目源代码树的全路径。例如,项目foo中的头文件foo/src/bar/baz.h按如下方式保护:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
…
#endif // FOO_BAR_BAZ_H_
注:不要使用#pragma once来防止重复包含,因为该指令是vc特有的,它是一个编译器指令,其它的编译器不保证能支持它,而#ifndef是语言层面的, 只要是支持c++的编译器, 都支持,跨平台,兼容性更好。
头文件依赖
使用前置声明(forward declarations)尽量减少.h文件中#include的数量,加快编译速度。
内联函数
?有当函数?有10行甚至更少时才会将其定义为内联函数(inline function)。
注:滥用内联将导致程序变慢。
函数参数顺序
定义函数时,参数顺序为:输入参数在前,输出参数在后。
包含文件的名称及次序
将包含次序标准化可增强可读性、避免隐藏依赖(注:隐藏依赖主要是指包含的文件编译)
头文件包含次序如下:C库、C++库、其他库的.h、项目内的.h。
实现文件(如dir/foo.cpp)包含次序如下:
dir2/foo2.h(优先位置,详情如下)
C系统文件
C++系统文件
其他库头文件
本项目内头文件
在包含文件里不要使用.(当前目录)和..(父目录),如:
#include “../abc.h” // 不要这样使用
作用域
命名空间
在.cpp文件中,允许甚至提倡使用未具名命名空间,以避免运行时的命名冲突: namespace {
// .cc 文件中 // 命名空间的内容无需缩进
enum { UNUSED, EOF, ERROR }; // 经常使用的符号
bool AtEof() { return pos_ == EOF; } // 使用本命名空间内的符号EOF
} // namespace
然而,不特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数,而不是未具名命名空间的成员。像上文展示的那样,不具名命名空间结束时用注释// namespace标识。
不能在.h文件中使用未具名命名空间。
不要在头文件中或者#include之前使用using namespace
局部变量
将局部变量尽可能置于最小作用域内,在声明变量时就将其初始化。
全局变量
禁止class类型的全局变量
内建类型的全局变量是允许的,
当然多线程代码中非常数全局变量也是被禁止的。
永远不要使用函数返回值来初始化全局变量。
原因:c++标准在全局变量的构造函数、析构函数和初始化顺序上没有保证。因此每次生成都可能会有变化,从而导致难以觉察的BUGS。
对于全局的字符串常量,使用C风格的字符串,而不要使用STL的字符串:
const char kFrogSays[] = “ribbet”;
虽然允许在全局作用域中使用全局变量,使用时务必三思。大多数全局变量应该是类的静态数据成员,或者当其?在.cpp文件中使用时,将其定义到未具名命名空间中
注:静态成员变量视作全局变量,所以,也不能是class类型!
如果一定要使用全局变量,请使用设计模式中的单件模式(singleton模式)。
C++类
用小类代替巨类
构造函数
类构造函数的初始化列表中成员变量初始化的顺序应该与在h头文件中声明的顺序一致。不要在构造函数体里进行赋值操作。
对单参数构造函数使用C++关键字explicit。
虚拟函数
避免在构造函数和析构函数中调用虚拟函数
继承
多用组合,少用继承
接口
接口定义如下:
class IFoo
{
public:
virtual ~IFoo(){};
virtual void dooo() = 0;
};
即含有纯虚函数,且实现虚析构函数的类。
接口类主要是为实现多态的。
操作符重载
尽量不要使用操作符重载
存取控制
将数据成员私有化,并提供相关存取函数
声明次序
在类中使用特定的声明次序:public:在private:之前,成员函数在数据成员(变量)前。
定义次序如下:public:、protected:、private:,如果那一块没有,直接忽略即可。
每一块中,声明次序一般如下:
1) typedefs和enums;
2) 常量;
3) 构造函数;
4) 析构函数;
5) 成员函数,含静态成员函数;
6) 数据成员,含静态数据成员。
编写短小函数
函数体尽量短小、紧凑,功能单一
智能指针
如果确实需要使用智能指针的话,scoped_ptr完全可以胜任。在非常特殊的情况下,例如对STL容器中对象,你应该只使用std::tr1::shared_ptr,任何情况下都不要使用auto_ptr。
引用参数
引用传递的参数必须加上const,如果要将参数传出,请使用指针。
缺省参数
禁止使用缺省函数参数。
异常
不要使用C++异常。
异常会导致程序控制流通过查看代码无法确定:函数有可能在不确定的地方返回,从而导致代码管理和调试困难。
异常的实用性可能会刺激开发人员在不恰当的时候抛出异常,戒者在不安全的地方从异常中恢复。
运行时类型识别RTTI
禁止使用RTTI, 单元测试程序除外。
类型转换
使用C++风格的类型转换, 不要使用C风格的类型转换。
1) static_cast:和C风格转换相似可做值的强制转换,或指针的父类到子类的明确的向上转换;
2) const_cast:移除const属性;
3) reinterpret_cast:指针类型和整型或其他指针间不安全的相互转换,仅在你对所做一切了然于心时使用;
4) dynamic_cast:除测试外不要使用,除单元测试外,如果你需要在运行时确定类型信息,说明设计有缺陷(参考RTTI)。
前置自增和自减
对简单数值(非对象)来说,两种都无所谓,对迭代器和模板类型来说,要使用前置自增(自减)。
const的使用
在任何可以使用的情况下都要使用const。
预处理宏
使用宏时要谨慎,尽量以内联函数、枚举和常量代替之,字符串化(##)除外;
0和NULL
整数用0,实数用0.0,?针用NULL,字符(串)用’/0’;
sizeof(sizeof)
用sizeof(varname)代替sizeof(type);
Boost库
只使用Boost中被认