1.1 头文件均使用 #define guards
#ifndef <PROJECT>_<PATH>_<FILE>_H_
#define <PROJECT>_<PATH>_<FILE>_H_
...
#endif // <PROJECT>_<PATH>_<FILE>_H_
1.2 包含所有使用到的头文件
只要头文件中引用了其他文件中的符号,就需要包含该文件;(即使通过传递包含已经包含了该文件)
例如:foo.cc 应该包含 bar.h (即使 foo.h 中包含了 bar.h)
1.3 前向声明
一般使用前向声明的地方,都是使用指针(指针占用空间固定的,不用包含头文件);
但前向声明会隐藏类之间的关系(例如继承关系),这在匹配函数时就可能引发不同的行为;
1.4 内联函数不要超过10行
注意析构函数,因为隐式的成员变量和基类析构函数调用,析构函数往往比它们看起来的要长!
不要包括循环 或 switch
1.5 头文件顺序
(1)项目中的头文件应该带路径(src 目录下直到该文件的路径,不要使用 . 或 . . )
最直接关联头文件(例如在 foo.cc 文件中,此处为 foo.h,以下每类均用空行分隔)
C库
C++库
其他库
该项目其他头文件
(2)在每个模块,应该按字母顺序排序
2. 作用域
2.1 命名空间
(1)不要使用 using 语句;
不要使用 inline namespaces;(会自动把内部的标识符放到外层作用域)
在头文件之后,包含整个源文件;
(2)其名字中,字母全部小写,单词使用下划线分隔;避免缩写
Top-level namespace 应该基于项目名字;
(3)不要在头文件中使用命名空间别名,除非其作用域是局部的(例如在函数内部)
命名空间中内容不需要缩进;
2.2 匿名命名空间 (Internal Linkage)
不会被外部文件使用的变量,可以将它们放入匿名命名空间中,或声明为 static ,不要在 .h 文件中使用这两种方法;
(1)2.3 ~ 2.5 还需要进一步学习
2.3 函数和变量
(1)非成员函数尽量放到某个命名空间中;
(2)声明时就初始化变量
2.4 静态变量和全局变量
const 代表 read-only , 但这并不意味着它是不可变的,也不意味着值总是相同的。
常量随着程序的运行其值不会改变;
C++17 变量可以被声明为 inline,来保证只有一份;
2.5 线程变量
thread_local 变量实际是一些对象的集合,不同的线程实际上访问不同的对象。
在类或命名空间中必须使用编译时常量初始化;
3. 类
3.1 构造函数
构造函数中不要调用虚函数,不要抛出异常(可能失败的初始化操作);
3.2 避免隐式转换
不要定义隐式转换,必须加上 explicit 修饰单参数构造函数和转换操作符;(这将阻止 隐式转换和列表初始化语法)
3.3 拷贝/移动
一个可复制的类应该显式声明复制操作(即使使用 = default
语法),一个仅移动的类应该明确声明移动操作,而一个不可复制/可移动的类则应该明确删除复制操作。
可复制的类也应该声明移动操作;
基类没有 private 部分,其可复制性/移动性由成员变量决定;
如果基类是不可复制/移动的,子类也是;
3.4 继承
尽可能使用组合关系,不得不使用继承的话,使用 public 继承;
虚函数最好都标注上 override 或 (less frequently) final
3.5 类成员变量都应该为 private,除非为常量;
3.6 命名规则
**全部小写,下划线区分单词; **
(1)类中成员变量最后有一个额外下划线;
(2)常量命名
const 或 constexpr 如果整个程序运行期间其值不变;以 k 开头;
(3)函数命名(类型名均采用这种方式 classes, structs, type aliases, enums)
每个单词首字母大写,不使用下划线
get 和 set 函数可以像变量一样命名
(4)枚举类型
enum class UrlTableError {
kOk = 0, // 类似常量以 k 开头,每个首字母大写
kOutOfMemory,
kMalformedInput,
};
3.7 注释
使用 空格 而非 Tab,可以设置 IDE 一个 Tab 改为2个空格
4. 函数
4.1 参数
仅输入作用的参数放在最前面;
(1)non-optional (必须传入的参数)
input parameters 使用 值传递 或 const 引用传递;
output and input/output parameters 使用引用传递;
(2)optional (可传可不传)
input parameters : 值传递时使用 std::optional
output and input/output parameters :使用 non-const 指针;
4.2 尽量小的函数
最好不要超过 40 行,如果超过拆分成小函数;
4.3 类型转换
除非转换为 void,否则都使用 cast ;
数值类型,使用int64_t{x}
intptr_t
指针大小的整数
char 类型的空使用 ‘\0’ ;
4.4 类型推导
如果使得代码更清晰或安全,才会使用。
例如在 auto ptr = make_shared<xxx>
中
4.5 switch
应该总是设置 default
凡是需要顺序往下执行的(不是执行完 case 后就 break 的情况),需要添加 [[fallthrough]];