头文件:
1、头文件自包含:自己编译,不依赖其他的.h文件进行编译,首选将模板和内联函数的定义放置在与其声明相同的文件中。这些构造的定义必须包含在使用它们的每个.c文件中,否则程序可能无法在某些构建配置中链接。
2、#define警卫:所有头文件应该有#define保护以防止多次包含。符号名称的格式应为<PROJECT>_<PATH>_<FILE>_H_。为了保证唯一性,它们应该基于项目源代码树中的完整路径。例如,项目foo中的文件foo/src/bar/baz.h应该具有以下警戒:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
#endif //FOO_BAR_BAZ_H_
3、前向声明:没有相关定义的类,函数或模板的声明。可以声明一个类而不定义它。(不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针或引用,或者用于声明 不是定义使用该类型作为形参类型或返回类型的函数)。
好处: 不必要的#include 会增加编译时间。
混乱随意的#include 可能导致循环#include ,可能出现编译错误。
隐藏依赖性,允许用户代码在其声明的这个类型的头文件更改时跳过必要的重新编译。
例子:
// b.h;
struct B {};
struct D : B {};
// good_user.cc;
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x) ;}
1、如果调用#include则会调用f(B*) 这个函数,因为会得到完整的继承类型,也会知道继承的关系,B* b = new D;指向一个基类指针的子类对象是可以的,子类是可以转成基类的。
2、如果使用前向声明,不包含#include 而是使用struct B; struct D;则会调用f(void*)这个函数,因为只知道B和D这两个类型,并不知道依赖关系,所以找不到对应的重载函数,就是调用f(void*),因为任何指针都可以赋值给void*而不需要强制类型转换。
4、内联函数
当函数很小时,比如10行或者更少时,才定义函数内联。(类中的成员函数都是内联函数)
虚、递归函数通常不内联,虚函数内联的主要原因是将其定义放在类中,为了方便或记录其行为。
5、
6、命名空间
尽量将代码放在命名空间中。命名空间应该具有基于项目名称以及可能的路径的唯一名称。不要使用内联命名空间。
命名空间将全局范围细分为不同的命名范围,因此可用于防止全局范围中的名称冲突。
内联命名空间主要用于跨版本的兼容性。
命名空间在include,gflags定义/声明和来自其他命名空间的类的前向声明之后封装整个源文件。
不要在命名空间std中声明任何内容,包括标准库类的前向声明。在命名空间std中声明实体是未定义的行为,即不可移植。要从标准库声明实体,请包含相应的头文件。
不要在头文件中的命名空间范围使用命名空间别名,因为导入到头文件中的命名空间中的任何内容都将成为该文件导出的公共API的一部分。
7、未命名的命名空间和静态变量
当.c文件中的定义不需要在该文件外引用时,将它们放在未命名的命名空间中或将它们声明为静态。不要在.h文件中使用这些构造。
所有声明都可以通过将它们放在未命名的命名空间中而被赋予内部链接,并且可以通过声明它们为静态来给予函数和变量内部链接。这意味着您声明的任何内容都无法从其他文件访问。如果不同的文件声明具有相同名称的东西,那么这两个实体是完全独立的。
对于不需要在其他地方引用的所有代码,提倡在.c文件中使用内部链接。不要在.h文件中使用内部链接。
8、非成员,静态成员和全局函数
9、局部变量
尽量在用的时候声明,并且应该使用初始化而不是声明和赋值,例如:
int j = g();
std::vector<int> v = {1, 2};
if,while和for语句所需的变量通常应在这些语句中声明,以使这些变量局限于这些范围。
如果是对象的话尽量声明在循环外部,不然每次进入该循环都会调用该对象的构造函数,离开循环开始下一次循环的时候会调用该对象的析构函数。
10、静态和全局变量
类:
1、在构造函数中工作
