Google C++代码规范
转载自公众号:天空的代码世界,微信号:tiankonguse
谷歌C++ Style Guide:https://google.github.io/styleguide/cppguide.html
1. C++命名代码规范:头文件
通常,每一个 .cc 文件都有一个对应的 .h 文件。
也有一些常见例外,如单元测试和只包含 main() 函数的小型 .cc 文件。
正确使用头文件可令代码在可读性、文件大小和性能上大为改观。
1.1 Self-contained 头文件
头文件应该是自给自足的(self-contained,可自编译的),以 .h 结尾。禁止分离出 -inl.h 头文件的做法。
自给自足的意思是用户和重构工具不需要为特别场合而包含额外的头文件和符号(symbols)。
1.2 头文件保护
所有头文件都应该使用 #define 或 #pragma once 来防止头文件被多重包含。
其中 #define 的命名格式当是 : <PROJECT>_<PATH>_<FILE>_H_。
使用 cpplint 工具来检查头文件保护,如果不符合要求,会有下面两种错误提示:
#ifndef header guard has wrong style, please use: <PROJECT>_<PATH>_<FILE>_H_ [build/header_guard]cpplint
#endif line should be "#endif // <PROJECT>_<PATH>_<FILE>_H_ [build/header_guard]cpplint
1.3前置声明
尽可能地避免使用前置声明。
使用 #include 包含需要的头文件。
定义:前置声明(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义。
优点:节省编译时间。
缺点:隐藏依赖关系,依赖升级可能会不兼容甚至引入某些 BUG。
例如对函数进行声明,后来加了一个参数,就会导致找不到函数。
如果对类进行声明,类进行调整时,甚至可能导致类的内存错误。
1.4 内联函数
只有当函数只有 10 行甚至更少时才将其定义为内联函数。
定义:当函数被声明为内联函数之后,编译器会将其内联展开,而不是按通常的函数调用机制进行调用。
优点:内联函数较小时,可以使代码性能更高(少一次函数调用)。
缺点:滥用内联函数导致程序变慢。
注意实现:递归和虚函数不能使用内联。
1.5 include 的路径及顺序
标准的头文件包含顺序可增强可读性,避免隐藏依赖。
要求头文件顺序为:相关头文件、C 库、C++ 库、其他库的 .h、本项目内的 .h。
项目内头文件应按照项目源代码目录树结构排列,避免使用 UNIX 特殊的快捷目录. (当前目录) 或 … (上级目录)。
例如 dir/foo.cc 或 dir/foo_test.cc 的主要作用是实现或测试 dir2/foo2.h 的功能,foo.cc 中包含头文件的次序如下:
dir2/foo2.h(优先位置,详情如下)
1. 空行
2. C 系统头文件
3. 空行
4. C++ 标准库头文件
5. 空行
6. 其他库的 `.h`文件
7. (可选)空行
8. 本项目内 `.h`文件
2. Google C++代码规范:命名规范
2.1 通用命名规则
名字应该可以看出来目的或者意图。
别心疼空间,毕竟相比之下让代码易于新读者理解更重要。
不要用只有项目开发者能理解的缩写,也不要通过砍掉几个字母来缩写单词。
一些特定的广为人知的缩写是允许的,例如用 i 表示迭代变量和用 T 表示模板参数。
2.2 文件命名
文件名要全部小写,可以包含下划线 “_” 或连字符 “-“,可以依照项目的约定来使用。
如果没有约定,那么 “_” 更好。
C++ 文件在项目内要统一以 .cc 或 .cpp 结尾,头文件以 .h 结尾。
专门插入文本的文件则以 .inc 结尾,参见1.头文件规范。
不要使用已经存在于 /usr/include 下的文件名,如 db.h。
通常应尽量让文件名更加明确。
例如:http_server_logs.h 就比 logs.h 要好。
定义类时文件名一般成对出现,如 foo_bar.h 和 foo_bar.cc,对应于类 FooBar。
2.3 类型命名
类型名称的每个单词首字母均大写,不包含下划线。
所有类型命名:类、结构体、类型定义 (typedef)、枚举、类型模板参数等均使用这个约定。
2.4 变量命名
变量 (包括函数参数) 和数据成员名一律小写,单词之间用下划线连接。
类的成员变量以下划线结尾,结构体除外。
2.5 常量命名
声明为 constexpr 或 const 的变量,或在程序运行期间其值始终保持不变的,命名时以 “k” 开头,大小写混合。
如果某些场景大写字母不能区分意图的话,才能使用下划线来区分。
例如:const int kAndroid8_0_0 = 24; // Android 8.0.0。
所有具有静态存储类型的变量(例如静态变量或全局变量)都应当以此方式命名(类和结构体中的变量呢?)。
对于其他存储类型的变量,如自动变量等,这条规则是可选的。
如果不采用这条规则,就按照一般的变量命名规则。
2.6 函数命名
一般来说,函数名的每个单词首字母大写(即“驼峰变量名”),没有下划线。
取值和设值函数的命名可以与变量名一致。
一般来说它们的名称与实际的成员变量对应,但并不强制要求。
例如 int count() 与 void set_count(int count)。
2.7命名空间命名
命名空间以小写字母与下划线命名。
顶级命名空间的名字应该基于项目名称。
另外,要注意避免嵌套命名空间的名字和常见的顶级命名空间的名字之间发生冲突。
2.8 枚举命名
枚举的命名应当和常量或宏一致:kEnumName 或是 ENUM_NAME。
由于宏模式可能会和宏冲突,对于新项目,建议使用常量模式。
2.9 宏命名
你并不打算使用宏,对吧?如果你一定要用,像这样命名:MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS_ALIKE。
参考预处理宏,通常不应该使用宏。如果不得不用,其命名全部由大写和下划线组成。
2.10 命名规则的特例
如果你命名的实体与已有 C/C++ 实体相似,可参考现有命名策略。
bigopen():函数名,参照 open() 的形式
uint:typedef
bigpos:struct 或 class,参照 pos 的形式
sparse_hash_map:STL 型实体,参照 STL 命名约定
LONGLONG_MAX:常量,如同 INT_MAX
2.11 最后
回顾一下,命名规范对文件、类型、变量、常量、函数、命名空间、枚举、宏等指定了确定性的要求。
而对于部分特殊场景,规范中也做了说明。
回顾一下有 5 个点大家要格外注意了。
一、变量名:不能使用驼峰式的格式,要使用下划线式的。
二、函数名:首个字母需要大写。
三、命名空间:不能使用大写。
四、常量:字母 k 前缀,之后驼峰式命名。
五、枚举:与常量命名保持一致。
3. Google C++代码规范:格式规范
3.1 格式规范
每个人都可能有自己的代码风格(style)和格式(formatting)。
但如果一个项目中的所有人都遵循同一风格的话,这个项目就能更顺利地进行。
每个人未必能同意下述的每一处格式规则,而且其中的不少规则需要一定时间的适应。
但整个项目服从统一的编程风格是很重要的,只有这样才能让所有人轻松地阅读和理解代码。
3.2 行长度
每一行代码最大字符数为 80。
下面 5 种情况允许一行超过 80 个字符。
- 不能换行的注释,比如含有命令 或者 url
- 明文字符串
- include 语句
- 头文件保护语句
- using 语句
3.3 非ASCII字符
尽量不使用非 ASCII 字符,使用时必须使用 UTF-8 编码。
非 ASCII 字符(中文等)只能用于注释当中,含有中文注释的代码文件必须使用 UTF-8 编码保存,不能有BOM头。
3.4 空格还是制表位
只使用空格,每次缩进 2 个空格。
不要在代码中使用制表符。你应该设置编辑器将制表符转为空格。
3.5 函数调用
允许3种格式,在一行内写完函数调用,在圆括号里对参数分行,或者参数另起一行且缩进四格。
原则上,尽可能精简行数,比如把多个参数适当地放在同一行里。
3.6 函数声明与定义
返回类型和函数名在同一行,参数也尽量放在同一行.
如果放不下就对形参分行,分行方式与函数调用 一致。
即第一个参数与函数名同行,换行的参数与第一个参数对齐;或者使用 4 个空格缩进来对齐。
注意实现:
-)左圆括号总是和函数名在同一行。
-)函数名和左圆括号间永远没有空格。
-)圆括号与参数间没有空格。
-)左大括号总在最后一个参数同一行的末尾处,不另起新行。
-)右大括号总是单独位于函数最后一行,或者与左大括号同一行。
-)右圆括号和左大括号间总是有一个空格。
3.7 条件语句
if 和左圆括号间都要有个空格。右圆括号和左大括号之间也要有个空格。
任何是否都需要加大括号。
if (condition) { // 圆括号里没有空格
... // 2 空格缩进
} else if (...) { // else 与 if 的右括号同一行
...
} else {
...
}
3.8 循环和switch
witch 语句可以使用大括号分段,以表明 cases 之间不是连在一起的。
switch 应该总是包含一个 default匹配,如果default 永远执行不到,简单的使用 assert语句。
switch (var) {
case 0: { // 2 空格缩进
... // 4 空格缩进
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
对于循环,左括号左边需要有空格,右括号右边也需要有空格。
for (int i = 0; i < kSomeNumber; ++i) {
printf("I take it back\n");
}
while (condition) {
// Repeat test until it returns false.
}
3.9 指针和引用
. 点和 -> 箭头前后不要有空格。
* 取值和 & 取址之后不能有空格。
声明指针和引用时,空格靠左靠右都可以。
x = *p;
p = &x;
x = r.y;
x = r->y;
char* c;
const string& str;
3.10 布尔表达式
如果一个布尔表达式超过标准行宽,断行方式要统一。
比如,逻辑操作符要么都在行尾,要么都在行首。
PS:Google 常见的格式是所有操作符放在行尾。
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one) {
...
}
3.11 空格与换行
-)永远不要在行尾添加没意义的留白。
-)左大括号前总是有空格
-)分号前不加空格
-)列表初始化中大括号内的空格是可选的,但要保持一致。
-)if else while for wtitch case 关键字都会有一个空格。
-)赋值左右都有空格
-)二元操作符左右都有空格,复杂的可以适当不加空格。
-)一元操作数与参数之间不需要加空格。
-)模板尖括号前后不需要加空格
-)一般首尾不要留空行,空行不能超过两行。
3.12 其他
返回值:不要在 return 表达式里加上非必须的圆括号。
命名空间:不缩进。
预处理指令:不要缩进,从行首开始。
类格式:
-)public 放在最前面,然后是 protected,最后是 private。
-)关键词 public, protected, private 要缩进 1 个空格。
-)除第一个关键词外,其他关键词前要空一行。
-)关键词后不要保留空行。