Gists
Google C++ Standard
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers/
Google C++ Standard
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers/
头文件
- 头文件应该能够自给自足(self-contained,也就是可以作为第一个头文件被引入),以 .h 结尾。至于用来插入文本的文件,说到底它们并不是头文件,所以应以 .inc 结尾。不允许分离出 -inl.h 头文件的做法。
- 所有头文件都应该有 #define 保护来防止头文件被多重包含,命名格式当是: <PROJECT><PATH><FILE>H
- 尽可能地避免使用前置声明。使用 #include 包含需要的头文件即可。
- 只有当函数只有 10 行甚至更少时才将其定义为内联函数。
- 使用标准的头文件包含顺序:相关头文件、C 库、C++ 库、其他库的头文件、本项目内的头文件。
作用域
- 鼓励在 .cc 文件内使用匿名命名空间或 static 声明。使用具名的命名空间时,其名称可基于项目名或相对路径。禁止使用 using 指示(using-directive)。禁止使用内联命名空间(inline namespace)。
- 在 .cc 文件中定义一个不需要被外部引用的变量时,可以将它们放在匿名命名空间或声明为 static。但是不要在 .h 文件中这么做。
- 使用静态成员函数或命名空间内的非成员函数,尽量不要用裸的全局函数。将一系列函数直接置于命名空间中,不要用类的静态方法模拟出命名空间的效果,类的静态方法应当和类的实例或静态数据紧密相关。
- 将函数变量尽可能置于最小作用域内,并在变量声明时进行初始化。
- 禁止定义静态储存周期非 POD 变量,禁止使用含有副作用的函数初始化 POD 全局变量,因为多编译单元中的静态变量执行时的构造和析构顺序是未明确的,这将导致代码的不可移植。
类
- 不要在构造函数中调用虚函数,也不要在无法报出错误时进行可能失败的初始化。
- 不要定义隐式类型转换。对于转换运算符和单参数构造函数,请使用 explicit 关键字。
- 如果你的类型需要,就让它们支持拷贝 / 移动。否则,就把隐式产生的拷贝和移动函数禁用。
- 仅当只有数据成员时使用 struct,其它一概使用 class。
- 使用组合常常比使用继承更合理。如果使用继承的话,定义为 public 继承。
- 真正需要用到多重实现继承的情况少之又少。只在以下情况我们才允许多重继承: 最多只有一个基类是非抽象类; 其它基类都是以 Interface 为后缀的 纯接口类。
- 接口是指只有纯虚函数和静态函数、没有非静态成员和非 protected 的构造函数的类,这些类以 Interface 为后缀 (不强制)。
- 除少数特定环境外,不要重载运算符。也不要创建用户定义字面量。
- 将所有数据成员声明为 private,除非是 static const 类型成员 (遵循 常量命名规则)。
- 将相似的声明放在一起,将 public 部分放在最前。
函数
- 我们倾向于按值返回, 否则按引用返回。 避免返回指针, 除非它可以为空。
- 倾向于编写简短,凝练的函数。
- 所有按引用传递的参数必须加上 const。
- 若要使用函数重载,则必须能让读者一看调用点就胸有成竹,而不用花心思猜测调用的重载函数到底是哪一种。这一规则也适用于构造函数。
- 只允许在非虚函数中使用缺省参数,且必须保证缺省参数的值始终一致。缺省参数与 函数重载 遵循同样的规则。一般情况下建议使用函数重载。
- 只有在常规写法 (返回类型前置) 不便于书写或不便于阅读时使用返回类型后置语法。
指针
- 动态分配出的对象最好有单一且固定的所有主,并通过智能指针传递所有权。
其他 C++ 特性
- 所有按引用传递的参数必须加上 const。
- 只在定义移动构造函数与移动赋值操作时使用右值引用。不要使用 std::forward。
- 不允许使用变长数组和 alloca()。
- 通常友元应该定义在同一文件内。
- 不使用 C++ 异常。
- 禁止使用 RTTI。
- 使用 C++ 的类型转换,如 static_cast<>()。不要使用 int y = (int)x 或 int y = int(x) 等转换方式;
- 只在记录日志时使用流。
- 对于迭代器和其他模板对象使用前缀形式 (++i) 的自增,自减运算符。
- 我们强烈建议你在任何可能的情况下都要使用 const。此外有时改用 C++11 推出的 constexpr 更好。
- 在 C++11 里,用 constexpr 来定义真正的常量,或实现常量初始化。
- C++ 内建整型中,仅使用 int。如果程序中需要不同大小的变量,可以使用 <stdint.h> 中长度精确的整型,如 int16_t。
- 使用宏时要非常谨慎,尽量以内联函数,引用,枚举和常量代替之。
- 整数用 0,实数用 0.0,指针用 nullptr 或 NULL,字符 (串) 用 '\0'。
- 尽可能用 sizeof(varname) 代替 sizeof(type)。
- 用 auto 绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
- 适当使用 lambda 表达式。别用默认 lambda 捕获,所有捕获都要显式写出来。
- 不要使用复杂的模板编程
- 只使用 Boost 中被认可的库,包括:Call Traits、Compressed Pair、BGL、Property Map、Iterator、Polygon、Bitmap、Statistic Distribution and Functions、Multi-index、Heap、Container。
命名约定
- 函数命名,变量命名,文件命名要有描述性; 少用缩写。
- 文件名要全部小写,可以包含下划线或连字符,依照项目的约定。如果没有约定,那么下划线更好。
- 类型名称的每个单词首字母均大写,不包含下划线。
- 变量 (包括函数参数) 和数据成员名一律小写,单词之间用下划线连接。类的成员变量以下划线结尾,但结构体的不用。
- 声明为 constexpr 或 const 的变量,或在程序运行期间其值始终保持不变的,命名时以 “k” 开头,大小写混合。
- 常规函数使用大小写混合。
- 取值和设值函数则要求与变量名匹配。
- 命名空间以小写字母命名。最高级命名空间的名字取决于项目名称。
- 枚举的命名应当和 常量 一致。
- 宏命名应使用全部大写加下划线分割单词。
注释
- 在每一个文件开头加入版权公告。
- 函数声明处的注释描述函数功能; 定义处的注释描述函数实现。
- 对于代码中巧妙的,晦涩的,有趣的,重要的地方加以注释。
- 对那些临时的,短期的解决方案,或已经够好但仍不完美的代码使用 TODO 注释;通过弃用注释(DEPRECATED comments)以标记某接口点已弃用;
格式化
- 每一行代码字符数不超过 80。
- 尽量不使用非 ASCII 字符,使用时必须使用 UTF-8 编码。
- 只使用空格,每次缩进 2 个空格。
- 返回类型和函数名在同一行,参数也尽量放在同一行,如果放不下就对形参分行,分行方式与 函数调用 一致。
- 要么一行写完函数调用,要么在圆括号里对参数分行,要么参数另起一行且缩进四格。如果没有其它顾虑的话,尽可能精简行数,比如把多个参数适当地放在同一行里。
- 怎么格式化函数调用,就怎么格式化 列表初始化。
- 倾向于不在圆括号内使用空格。关键字 if 和 else 另起一行。
- switch 语句可以使用大括号分段,以表明 cases 之间不是连在一起的。在单语句循环里,括号可用可不用。空循环体应使用 {} 或 continue。
- 句点或箭头前后不要有空格。指针/地址操作符 (*,&) 之后不能有空格。
- 布尔运算符如逻辑与总位于行尾:
- 预处理指令不要缩进,从行首开始。
- 访问控制块的声明依次序是 public:,protected:,private:,每个都缩进 1 个空格。
- 构造函数初始化列表放在同一行或按四格缩进并排多行。
- 命名空间内容不缩进。
- 水平留白的使用根据在代码中的位置决定。永远不要在行尾添加没意义的留白。
- 垂直留白越少越好。
规则特例
- 对于现有不符合既定编程风格的代码可以网开一面。
- Windows 程序员有自己的编程习惯,主要源于 Windows 头文件和其它 Microsoft 代码。
- 运用常识和判断力,并且 保持一致。