C++风格规范(google风格整理)

C++风格规范(google风格整理)

一、头文件

1.1 define保护

所有头文件都应该使用 #define 来防止头文件被多重包含, 命名格式当是:

<PROJECT>_<PATH>_<FILE>_H_
例子:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

#endif // FOO_BAR_BAZ_H_

1.2 前置声明

尽可能地避免使用前置声明。使用 #include 包含需要的头文件即可。

1.3 内联函数

只有当函数只有 10 行甚至更少时才将其定义为内联函数.

1.4 #include 的路径及顺序

使用标准的头文件包含顺序可增强可读性, 避免隐藏依赖。
顺序:

  • dir2/foo2.h (优先位置, 详情如下)
  • C 系统文件
  • C++ 系统文件
  • 其他库的 .h 文件
  • 本项目内 .h 文件

二、作用域

2.1 命名空间

鼓励在 .cc 文件内使用匿名命名空间或 static 声明. 使用具名的命名空间时, 其名称可基于项目名或相对路径. 禁止使用 using 指示(using-directive)。禁止使用内联命名空间(inline namespace)。
命名规则:

  • 遵守 命名空间命名 中的规则。
  • 在命名空间的最后注释出命名空间的名字。
  • 用命名空间把文件包含,类的前置声明以外的整个源文件封装起来。
  • 不要在命名空间 std 内声明任何东西, 包括标准库的类前置声明. 在 std 命名空间声明实体是未定义的行为, 会导致如不可移植. 声明标准库下的实体, 需要包含对应的头文件.
  • 不应该使用 using 指示 引入整个命名空间的标识符号。
  • 不要在头文件中使用 命名空间别名 除非显式标记内部命名空间使用。因为任何在头文件中引入的命名空间都会成为公开API的一部分。
    namespace baz = ::foo::bar::baz;
  • 禁止用内联命名空间

三、类

3.1 构造函数的职责

不要在构造函数中调用虚函数, 也不要在无法报出错误时进行可能失败的初始化.

3.2 隐式类型转换

不要定义隐式类型转换. 对于转换运算符和单参数构造函数, 请使用 explicit 关键字.
定义:
隐式类型转换允许一个某种类型 (称作 源类型) 的对象被用于需要另一种类型 (称作 目的类型) 的位置。例如, 将一个 int 类型的参数传递给需要 double 类型的函数.

3.3 可拷贝类型和可移动类型

如果你的类型需要, 就让它们支持拷贝 / 移动. 否则, 就把隐式产生的拷贝和移动函数禁用.
可拷贝类型允许对象在初始化时得到来自相同类型的另一对象的值, 或在赋值时被赋予相同类型的另一对象的值, 同时不改变源对象的值. 对于用户定义的类型, 拷贝操作一般通过拷贝构造函数与拷贝赋值操作符定义. string 类型就是一个可拷贝类型的例子.

3.4 结构体 VS. 类

仅当只有数据成员时使用 struct, 其它一概使用 class.

3.5 继承

使用组合 (YuleFox 注: 这一点也是 GoF 在 <> 里反复强调的) 常常比使用继承更合理. 如果使用继承的话, 定义为 public 继承.

3.6 多重继承

真正需要用到多重实现继承的情况少之又少. 只在以下情况我们才允许多重继承: 最多只有一个基类是非抽象类; 其它基类都是以 Interface 为后缀的 纯接口类。

3.7 接口

接口是指满足特定条件的类, 这些类以 Interface 为后缀 (不强制).

3.8 运算符重载

除少数特定环境外, 不要重载运算符. 也不要创建用户定义字面量.

3.9 存取控制

将 所有 数据成员声明为 private, 除非是 static const 类型成员 (遵循 常量命名规则). 处于技术上的原因, 在使用 Google Test 时我们允许测试固件类中的数据成员为 protected.

3.10 声明顺序

将相似的声明放在一起, 将 public 部分放在最前.

四、函数

4.1 参数顺序

函数的参数顺序为: 输入参数在先, 后跟输出参数.

4.2 编写简短函数

我们倾向于编写简短, 凝练的函数.

4.3 引用参数

参数尽量使用引用传递,且所有按引用传递的参数必须加上 const,引用的参数不修改,要修改的参数用指针。

4.4 函数重载

若要使用函数重载, 则必须能让读者一看调用点就胸有成竹, 而不用花心思猜测调用的重载函数到底是哪一种. 这一规则也适用于构造函数.

4.5 缺省参数

有些函数一般情况下使用默认参数, 但有时需要又使用非默认的参数. 缺省参数为这样的情形提供了便利, 使程序员不需要为了极少的例外情况编写大量的函数. 和函数重载相比, 缺省参数的语法更简洁明了, 减少了大量的样板代码, 也更好地区别了 “必要参数” 和 “可选参数”.

4.6 函数返回类型后置

只有在常规写法 (返回类型前置) 不便于书写或不便于阅读时使用返回类型后置语法.
auto foo(int x) -> int;

五、来自Google的奇技

5.1 所有权与智能指针

动态分配出的对象最好有单一且固定的所有主, 并通过智能指针传递所有权.

5.2 Cpplint

使用 cpplint.py 检查风格错误.

六、其他C++特性

6.1 引用参数

所有按引用传递的参数必须加上 const.

6.2 右值引用

只在定义移动构造函数与移动赋值操作时使用右值引用. 不要使用 std::forward.

6.3 函数重载

若要用好函数重载,最好能让读者一看调用点(call site)就胸有成竹,不用花心思猜测调用的重载函数到底是哪一种。该规则适用于构造函数。

6.4 缺省参数

我们不允许使用缺省函数参数,少数极端情况除外。尽可能改用函数重载。

6.5 变长数组和 alloca()

我们不允许使用变长数组和 alloca().

6.6 友元

我们允许合理的使用友元类及友元函数.

6.7 异常

我们不使用 C++ 异常.

6.8 运行时类型识别

我们禁止使用 RTTI.

6.9 类型转换

使用 C++ 的类型转换, 如 static_cast<>(). 不要使用 int y = (int)x 或 int y = int(x) 等转换方式。

6.10 流

只在记录日志时使用流.

6.11 前置自增和自减

对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.

6.12 const 用法

我们强烈建议你在任何可能的情况下都要使用 const. 此外有时改用 C++11 推出的 constexpr 更好。

6.13 constexpr 用法

在 C++11 里,用 constexpr 来定义真正的常量,或实现常量初始化。

6.14 整型

C++ 内建整型中, 仅使用 int. 如果程序中需要不同大小的变量, 可以使用 <stdint.h> 中长度精确的整型, 如 int16_t.如果您的变量可能不小于 2^31 (2GiB), 就用 64 位变量比如 int64_t. 此外要留意,哪怕您的值并不会超出 int 所能够表示的范围,在计算过程中也可能会溢出。所以拿不准时,干脆用更大的类型。

6.15 64 位下的可移植性

代码应该对 64 位和 32 位系统友好. 处理打印, 比较, 结构体对齐时应切记:

  • 对于某些类型, printf() 的指示符在 32 位和 64 位系统上可移植性不是很好.
  • 记住 sizeof(void *) != sizeof(int). 如果需要一个指针大小的整数要用 intptr_t.
  • 你要非常小心的对待结构体对齐, 尤其是要持久化到磁盘上的结构体。
  • 创建 64 位常量时使用 LL 或 ULL 作为后缀。
  • 如果你确实需要 32 位和 64 位系统具有不同代码, 可以使用 #ifdef _LP64 指令来切分 32/64 位代码. (尽量不要这么做, 如果非用不可, 尽量使修改局部化)。

6.16 预处理宏

使用宏时要非常谨慎, 尽量以内联函数, 枚举和常量代替之.

6.17 0, nullptr 和 NULL

整数用 0, 实数用 0.0, 指针用 nullptr 或 NULL, 字符 (串) 用 ‘\0’.
整数用 0, 实数用 0.0, 这一点是毫无争议的.
对于指针 (地址值), 到底是用 0, NULL 还是 nullptr. C++11 项目用 nullptr; C++03 项目则用 NULL, 毕竟它看起来像指针。实际上,一些 C++ 编译器对 NULL 的定义比较特殊,可以输出有用的警告,特别是 sizeof(NULL) 就和 sizeof(0) 不一样。
字符 (串) 用 ‘\0’, 不仅类型正确而且可读性好.

6.18 sizeof

尽可能用 sizeof(varname) 代替 sizeof(type).

使用 sizeof(varname) 是因为当代码中变量类型改变时会自动更新. 您或许会用 sizeof(type) 处理不涉及任何变量的代码,比如处理来自外部或内部的数据格式,这时用变量就不合适了。

6.19 auto

用 auto 绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。

6.20 列表初始化

用列表初始化。

6.21 Lambda 表达式

适当使用 lambda 表达式。别用默认 lambda 捕获,所有捕获都要显式写出来。

6.22 模板编程

不要使用复杂的模板编程

6.23 Boost 库

只使用 Boost 中被认可的库.

6.24 C++11

适当用 C++11(前身是 C++0x)的库和语言扩展,在贵项目用 C++11 特性前三思可移植性。

七 命名约定

7.1 通用命名规则

函数命名, 变量命名, 文件命名要有描述性; 少用缩写.

7.2 文件命名

文件名要全部小写, 可以包含下划线 () 或连字符 (-), 依照项目的约定. 如果没有约定, 那么 “” 更好.

7.3 类型命名

类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum.

7.4 变量命名

变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.

7.5 常量命名

声明为 constexpr 或 const 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合.

7.6 函数命名

常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().

7.7 命名空间命名

命名空间以小写字母命名. 最高级命名空间的名字取决于项目名称. 要注意避免嵌套命名空间的名字之间和常见的顶级命名空间的名字之间发生冲突.

7.8 枚举命名

枚举的命名应当和 常量 或 宏 一致: kEnumName 或是 ENUM_NAME.

7.9 宏命名

你并不打算 使用宏, 对吧? 如果你一定要用, 像这样命名: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

八、注释

8.1 注释风格

使用 // 或 /* */, 统一就好.

8.2 文件注释

在每一个文件开头加入版权公告.

文件注释描述了该文件的内容. 如果一个文件只声明, 或实现, 或测试了一个对象, 并且这个对象已经在它的声明处进行了详细的注释, 那么就没必要再加上文件注释. 除此之外的其他文件都需要文件注释.

8.3 类注释

每个类的定义都要附带一份注释, 描述类的功能和用法, 除非它的功能相当明显.

8.4 函数注释

函数声明处的注释描述函数功能; 定义处的注释描述函数实现。
函数声明处注释的内容:

  • 函数的输入输出.
  • 对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
  • 函数是否分配了必须由调用者释放的空间.
  • 参数是否可以为空指针.
  • 是否存在函数使用上的性能隐患.
  • 如果函数是可重入的, 其同步前提是什么?

8.5 变量注释

通常变量名本身足以很好说明变量用途. 某些情况下, 也需要额外的注释说明

8.6 实现注释

对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释.

8.7 标点, 拼写和语法

注意标点, 拼写和语法; 写的好的注释比差的要易读的多.

8.8 TODO 注释

对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 TODO 注释.

8.9 弃用注释

通过弃用注释(DEPRECATED comments)以标记某接口点已弃用.

九、格式

9.1 行长度

每一行代码字符数不超过 80.

9.2 非 ASCII 字符

尽量不使用非 ASCII 字符, 使用时必须使用 UTF-8 编码.

9.3 空格还是制表位

只使用空格, 每次缩进 2 个空格.

9.4 函数声明与定义

返回类型和函数名在同一行, 参数也尽量放在同一行, 如果放不下就对形参分行, 分行方式与 函数调用 一致.

9.5 Lambda 表达式

Lambda 表达式对形参和函数体的格式化和其他函数一致; 捕获列表同理, 表项用逗号隔开.

9.6 函数调用

要么一行写完函数调用, 要么在圆括号里对参数分行, 要么参数另起一行且缩进四格. 如果没有其它顾虑的话, 尽可能精简行数, 比如把多个参数适当地放在同一行里.

9.7 列表初始化格式

您平时怎么格式化函数调用, 就怎么格式化 列表初始化.

9.8 条件语句

倾向于不在圆括号内使用空格. 关键字 if 和 else 另起一行.

9.9 循环和开关选择语句

switch 语句可以使用大括号分段, 以表明 cases 之间不是连在一起的. 在单语句循环里, 括号可用可不用. 空循环体应使用 {} 或 continue.

9.10 指针和引用表达式

句点或箭头前后不要有空格. 指针/地址操作符 (*, &) 之后不能有空格.

9.11 布尔表达式

如果一个布尔表达式超过 标准行宽, 断行方式要统一一下.

9.12 函数返回值

不要在 return 表达式里加上非必须的圆括号.

9.13 变量及数组初始化

用 =, () 和 {} 均可.

9.14 预处理指令

预处理指令不要缩进, 从行首开始.

9.15 类格式

访问控制块的声明依次序是 public:, protected:, private:, 每个都缩进 1 个空格.

9.16 构造函数初始值列表

构造函数初始化列表放在同一行或按四格缩进并排多行.

MyClass::MyClass(int var) : some_var_(var) {
DoSomething();
}

// 如果不能放在同一行,
// 必须置于冒号后, 并缩进 4 个空格
MyClass::MyClass(int var)
: some_var_(var), some_other_var_(var + 1) {
DoSomething();
}

9.17 命名空间格式化

命名空间内容不缩进.

9.18 水平留白

水平留白的使用根据在代码中的位置决定. 永远不要在行尾添加没意义的留白.

9.19 垂直留白

垂直留白越少越好

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值