1. 头文件
1.1. #define保护
define
保护的命名格式必须是
。既可以保证在项目内部不重复,也可以保证哪天代码拷贝到其他项目中,也不冲突。
别使用下划线开头
1.2 前置声明
类型声明,没有伴随定义。能够节省编译时间,避免不必要的重新编译。
- 尽量避免前置声明那些第三方的实体。
- 函数和类模板,优先使总用
1.3 内联函数
只有当函数非常简短,或者性能关键的函数,才鼓励使用内联函数。否则只会增大代码体积。
不要内联超过10行的函数
1.4 #include
的路径及顺序
使用标准的包含顺序,增强可读性和避免隐藏依赖:相关头文件,C库,C++库,其他库的.h,本项目内的.h
// foo.cc
#include "base/foo.h
#include
#include
#include
#include
避免使用特殊的快捷目录.
和..
。也不要重复引入foo.h
中已经包含的头文件。
foo.h
依赖哪些头文件,就必须包含哪些头文件。例如,foo.h
依赖vector
,bar.h
里包含了vector
,那么如果你在这么写,是不会有问题的
// foo.c
#include "base/bar.h"
#include "base/foo.h"
但是别人用foo.h
是很可能会有编译问题,原因是bar.h
隐藏了foo.h
对vector
的依赖关系。
平台特定代码需要条件编译的,可以放到左右include之后,但最简练的做法是放到一个平台相关定义的头文件中,比如:
#include "base/foo.h"
#include "base/port.h" // platform specific defines
2. 作用域
2.1 名字空间
鼓励使用命名空间,但是同一个项目最好不要有多个命名空间,可以基于项目名。最好不要对非std的命名空间使用using指示。可以使用using声明。
2.2 嵌套类
当嵌套类只被外围类使用时,把它作为外围类作用域内的成员,而不是去污染外部作用域的同名类。除非是公共接口的一部分,否则将嵌套类定义成私有。
// foo.h
class Foo {
private:
class Bar {
...
};
};
2.3 非成员函数、静态成员函数和全局函数
使用静态成员函数或名字空间内的非成员函数,尽量不要用裸的全局函数。
有时候将函数的定义同类的实例脱钩是有益的,甚至是必要的。相比单纯为了封装若干个不共享任何静态数据的静态成员函数而创建类,不如使用名字空间。(注,那些著名的XxxUtils类就是绝佳的反例)
2.4 局部变量
将函数变量尽可能置于最小作用域内,并在变量声明时进行初始化。
for (int i = 0; i < 10; ++i)
...
while (const char* p = stretch(str, '/)) str = p + 1;
如果变量是对象,则在循环体外声明这类变量会高效的多:
Foo f;
for (int i = 0; i < 10; ++i) {
// Foo f; // Don't do this
f.DoSomething(i);
}
2.5 静态和全局变量
禁止使用class类型的静态或全局变量,它们会导致难以发现的bug和不确定的构造和析构函数调用顺序。例如,当程序结束时,某个静态变量已经析构了,但其他线程试图访问它(自带线程的类很容易犯这类问题)。
如果你确实需要一个class类型的静态或全局变量,可以考虑在main()
函数或pthread_once()
内初始化一个指针且永不回收。
3. 类
3.1 构造函数的职责
不要在构造函数中进行复杂的初始化(尤其那些可能失败或者需要调用虚函数的初始化)
简单的初始化用类成员初始化完成,保证对象处于正确的状态。
3.2 显示构造函数
对单个参数的构造函数使用explicit
关键字
避免不合时宜的隐式转换。
3.3 可拷贝和可赋值类型
3.7 继承
优先使用组合。如果使用继承的话,定义为public
继承。不要过度使用实现继承。
如果你的类有虚函数,则析构函数也应该为虚函数。注意数据成员任何情况下都必须是私有的。
只有当所有父类除第一个外都是纯接口类时,才允许使用多继承。纯接口类必须以Interface
为后缀(不强制,但是要能和普通类区分开来)。
纯接口类是只有纯虚函数和静态函数的类,没有非静态数据成员,并且不能实例化。
3.8
除了少数特定环境,不要重载运算符。
业务逻辑基本不需要重载运算符。
class Foo
{
public:
const string& get_name() const;
}
公共声明放在前面,私有声明方后面。
不会改变类属性的函数或成员,全部定义为const。通过声明表明你的意图。
参数和返回值尽量使用常量引用
const Foo &
。如果是通用库,请使用namespace保护,避免和其他名字冲突。
谨慎对待带指针成员的函数,如果有,考虑复制、赋值和析构时,指针资源的处理(不懂请务必查阅Effective C++。
有参数的构造函数声明为explicit,除非有足够的理由不这么做。
5. 其他C++特性
5.1 引用参数
所有按引用传递的参数必须加上const。
输入参数是值参或const引用,输出参数为指针。输入参数可以是const指针,但绝对不能是非const的引用参数,除非用于交换,比如swap().