虽说是编程风格指南,但是干货也不少,很多C++的实用技术在里面。
头文件
通常每一个.cpp文件都对应一个.h文件;#define保护
所有头文件都应该使用#define防止头文件被多重包含,为保证唯一性,头文件的命名应该依据所在项目源码树的全部路径。
能用前置声明的地方尽量不用#include
当一个头文件被包含的同时也引入了新的依赖,一旦该头文件被修改,代码就会被重新编译,如果这个头文件又包含了其他头文件,这些文件的任何改变都将导致所有包含该头文件的代码被重新编译。
如果头文件中用到类File,但不需要访问File类的声明,头文件只需要前置声明class File,而无需#include。
有时,使用指针成员替代对象成员是明智之举,然后这会降低代码可读性及效率。
内联函数
只有当函数只有10行甚至更少的时候才将其定义为内联函数,性能关键的函数,建议内联,不能包含switch语句。
复杂的内联函数的定义,应放在后缀名为 inl.h 的头文件中。
定义函数,参数顺序依次为:输入参数->输出参数,输入参数一般传值或者传const引用,输出参数则是非const指针。
#include的路径和顺序
使用标准的头文件包含顺序可增强可读性,避免隐藏依赖。顺序:C库,C++库,其他库.h,本项目.h.
包含文件的次序除了美观之外,最重要的就是减少隐藏依赖,使每个头文件在“最需要编译的地方编译”。
作用域
鼓励在.cpp文件中使用匿名名字控件,使用具名的名字空间时,其名称可基于项目名或相对路径,不要使用using关键字。
命名空间将全局作用域细分为独立的,具名作用域可有效防止全局作用域的命名冲突。
用命名空间把文件包含,gflags的声明和定义,以及类的前置声明以外的整个文件封装起来,以区别其他命名控件。
最好不要使用using关键字,以保证名字空间下的所有名称都可以正常使用。
在.cpp和.h的函数,方法和类中可以使用using关键字。
嵌套类
当共有嵌套类作为接口的一部分时,虽然可以直接将他们保持在全局作用域中,但将潜逃类的声明置于命名空间内是更好的选择。
不要将嵌套类定义成公有,除非它们是接口的部分,比如嵌套类含有某些方法的一组选项。
非成员函数,静态成员函数和全局函数
使用静态成员函数或名字空间内的非成员函数,尽量不要用全裸的全局函数,如果必须定义非成员函数,又只是在.cc文件中使用它,可以用匿名名字空间或static链接关键字,限定其作用域。
局部变量
将函数变量尽可能置于小的作用域内,并在变量声明时进行初始化。在循环中放置一个对象,则每次进入都要构造,离开都要析构。
静态和全局变量
禁止使用class类型的静态或全局变量,它们会导致很难发现的bug和不确定的构造和析构函数调用顺序。
永远不要使用函数返回值初始化静态变量;不要在多线程代码中使用非const的静态变量。
作用域的使用除了考虑名称污染,可读性之外,主要是为了降低耦合,提高编译/执行效率。一般不要重载运算符,缺点是会混淆视听,难以调试。如果需要的话,可以定义类似Equals(), CopyFrom()等函数。
将所有数据成员声明为private,并依据需要提供相应的存取函数,一般在头文件中存取函数定义成inline。
类的访问控制区段的声明顺序为public->protected->private.每个区段的声明顺序:typedefs和枚举->常量->构造函数->析构函数->成员函数->数据成员。
函数尽量短小,紧凑,功能单一。
如果函数超过40行,可以考虑能不能在不影响程序结构的前提下进行分割。
尽量避免使用智能指针,使用时尽量局部化,安全第一。
如果一定要用智能指针,scoped-ptr完全可以胜任,只在非常特定的情况下使用shared-ptr,任何时候都不要用auto_ptr;
其他C++特性
所有按引用传值的参数必须加上const。
输入参数是传值或者const引用,输出参数用指针。
仅在输入参数类型不同,功能相同时使用重载函数,不要用函数重载模拟缺省函数参数。
规定所有参数必须明确指定,迫使程序员理解API和各参数值的意义,避免在不深入理解函数的情况下使用。
不要使用变长数组和alloc(),建议使用安全的分配器。
友元扩大了类的封装边界,某些情况下,相对域将类成员声明为public,使用友元是更好的选择,尤其是如果只允许另一个类访问该类的私有成员时。
隐藏,覆盖和重载
隐藏 - 在基类中的某些函数,如果没有virtual关键字关键字,和其基类中的函数名一样,那么在子类中,基类的所有同名函数将被隐藏。
覆盖(override) - 派生类的虚函数覆盖了基类的同名具参数相同的函数,要一模一样,指的是函数名,参数,返回类型。
相同的函数名的函数,在基类和派生类中的关系只能是覆盖或者隐藏。
重载 - 必须在一个域中,函数名称相同,但是函数参数不同。
除非单元测试,否则不要使用RTTI,如果发祥自己不得不写些行为逻辑取决于对象类型的代码,考虑换一种方式判断对象类型。
类型转换不要使用c风格类型转换,而应该使用C++风格。
1.static_cast特换c风格值转换,或某个类指针明确向上转换为父级指针;
2.const_cast去掉const属性;
3.reinterpret_cast指针类型和整数类型或其他指针之间进行不安全的相互转换;
4.测试代码之外不要用dynamic_cast.
不要使用流,除非是日志接口需要,使用printf之类的代替,使用流还有很多利弊,但代码一致性胜过一切。
对于迭代器和其它模板对象,使用前缀形式(++i)的自增/自减运算符。
常量命名在名称前加k。
在任何可能的情况下使用const,类函数加上const表明该类不会修改成员变量的状态。
如果数据成员在对象构造之后再也不会发生变化,将其定义为const。
使用断言来指出变量为非负,而不是使用无符号型。
如果数据成员在对象构造之后再也不会发生变化,将其定义为const。
使用宏时要非常谨慎,尽量以内联函数/枚举/常量代替。
宏意味着你和编译器看到的代码时不同的,可能会导致异常行为,尤其时因为其具有全局作用域。
不要在.h文件中定义宏
不要试图使用展开后会导致c++不稳定的宏,至少要附上文档说明行为。
文件注释可以炫耀你的成就,也为了捅了篓子别人可以找你。
每一行代码字符不超过80个。
只使用空格作为缩进符。
函数声明和实现处的所有形参,名称必须保持一致。
return表达式不要用圆括号包围。