• 格式
    • 行宽
      • 建议3.1.1 行宽不超过 120 个字符
    • 缩进
      • 规则3.2.1 使用空格进行缩进,每次缩进2个空格
    • 大括号
      • 规则3.3.1 除函数外,使用 K&R 缩进风格
    • 函数声明和定义
      • 规则3.4.1 函数声明和定义的返回类型和函数名在同一行;函数参数列表超出行宽时要换行并合理对齐
    • 函数调用
      • 规则3.5.1 函数调用入参列表应放在一行,超出行宽换行时,保持参数进行合理对齐
    • if语句
      • 规则3.6.1 if语句必须要使用大括号
      • 规则3.6.2 禁止 if/else/else if 写在同一行
    • 循环语句
      • 规则3.7.1 循环语句要求使用大括号
    • switch语句
      • 规则3.8.1 switch 语句的 case/default 要缩进一层
    • 表达式
      • 建议3.9.1 表达式换行要保持换行的一致性,运算符放行末
    • 变量赋值
      • 规则3.10.1 多个变量定义和赋值语句不允许写在一行
    • 初始化
      • 规则3.11.1 初始化换行时要有缩进,并进行合理对齐
    • 指针与引用
      • 建议3.12.1 指针类型"*"跟随变量名或者类型,不要两边都留有或者都没有空格
      • 建议3.12.2 引用类型"&"跟随变量名或者类型,不要两边都留有或者都没有空格
    • 编译预处理
      • 规则3.13.1 编译预处理的"#"统一放在行首,嵌套编译预处理语句时,"#"不缩进
    • 空格和空行
      • 建议3.14.1 水平空格应该突出关键字和重要信息,避免不必要的留白
      • 建议3.14.2 合理安排空行,保持代码紧凑
      • 规则3.15.1 类访问控制块的声明依次序是 public:, protected:, private:,每个都缩进 1 个空格
      • 规则3.15.2 构造函数初始化列表放在同一行或按四格缩进并排多行

    格式

    尽管有些编程的排版风格因人而异,但是我们强烈建议和要求使用统一的编码风格,以便所有人都能够轻松的阅读和理解代码,增强代码的可维护性。

    行宽

    建议3.1.1 行宽不超过 120 个字符

    建议每行字符数不要超过 120 个。如果超过120个字符,请选择合理的方式进行换行。

    例外:

    • 如果一行注释包含了超过120 个字符的命令或URL,则可以保持一行,以方便复制、粘贴和通过grep查找;
    • 包含长路径的 #include 语句可以超出120 个字符,但是也需要尽量避免;
    • 编译预处理中的error信息可以超出一行。预处理的 error 信息在一行便于阅读和理解,即使超过 120 个字符。
    1. #ifndef XXX_YYY_ZZZ
    2. #error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    3. #endif

    缩进

    规则3.2.1 使用空格进行缩进,每次缩进2个空格

    只允许使用空格(space)进行缩进,每次缩进为 2 个空格。

    大括号

    规则3.3.1 除函数外,使用 K&R 缩进风格

    K&R风格函数左大括号跟随语句放行末。右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。

    如:

    1. struct MyType { // 跟随语句放行末,前置1空格
    2. ...
    3. };
    4. int Foo(int a) { // 函数左大括号跟随语句放行末
    5. if (...) {
    6. ...
    7. } else {
    8. ...
    9. }
    10. }

    推荐这种风格的理由:

    • 代码更紧凑;
    • 相比另起一行,放行末使代码阅读节奏感上更连续;
    • 符合后来语言的习惯,符合业界主流习惯;
    • 现代集成开发环境(IDE)都具有代码缩进对齐显示的辅助功能,大括号放在行尾并不会对缩进和范围产生理解上的影响。对于空函数体,可以将大括号放在同一行:
    1. class MyClass {
    2. public:
    3. MyClass() : value(0) {}
    4. private:
    5. int value;
    6. };

    函数声明和定义

    规则3.4.1 函数声明和定义的返回类型和函数名在同一行;函数参数列表超出行宽时要换行并合理对齐

    在声明和定义函数的时候,函数的返回值类型应该和函数名在同一行;如果行宽度允许,函数参数也应该放在一行;否则,函数参数应该换行,并进行合理对齐。参数列表的左圆括号总是和函数名在同一行,不要单独一行;右圆括号总是跟随最后一个参数。

    换行举例:

    1. ReturnType FunctionName(ArgType paramName1, ArgType paramName2) { // Good:全在同一行
    2. ...
    3. }
    4. ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // 行宽不满足所有参数,进行换行
    5. ArgType paramName2, // Good:和上一行参数对齐
    6. ArgType paramName3) {
    7. ...
    8. }
    9. ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // 行宽限制,进行换行
    10. ArgType paramName3, ArgType paramName4, ArgType paramName5) { // Good: 换行后 4 空格缩进
    11. ...
    12. }
    13. ReturnType ReallyReallyReallyReallyLongFunctionName( // 行宽不满足第1个参数,直接换行
    14. ArgType paramName1, ArgType paramName2, ArgType paramName3) { // Good: 换行后 4 空格缩进
    15. ...
    16. }

    函数调用

    规则3.5.1 函数调用入参列表应放在一行,超出行宽换行时,保持参数进行合理对齐

    函数调用时,函数参数列表放在一行。参数列表如果超过行宽,需要换行并进行合理的参数对齐。左圆括号总是跟函数名,右圆括号总是跟最后一个参数。

    换行举例:

    1. ReturnType result = FunctionName(paramName1, paramName2); // Good:函数参数放在一行
    2. ReturnType result = FunctionName(paramName1,
    3. paramName2, // Good:保持与上方参数对齐
    4. paramName3);
    5. ReturnType result = FunctionName(paramName1, paramName2,
    6. paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进
    7. ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行
    8. paramName1, paramName2, paramName3); // 换行后,4 空格缩进

    如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。

    1. // Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解
    2. int result = DealWithStructureLikeParams(left.x, left.y, // 表示一组相关参数
    3. right.x, right.y); // 表示另外一组相关参数

    if语句

    规则3.6.1 if语句必须要使用大括号

    我们要求if语句都需要使用大括号,即便只有一条语句。

    理由:

    • 代码逻辑直观,易读;
    • 在已有条件语句代码上增加新代码时不容易出错;
    • 对于在if语句中使用函数式宏时,有大括号保护不易出错(如果宏定义时遗漏了大括号)。
    1. if (objectIsNotExist) { // Good:单行条件语句也加大括号
    2. return CreateNewObject();
    3. }

    规则3.6.2 禁止 if/else/else if 写在同一行

    条件语句中,若有多个分支,应该写在不同行。

    如下是正确的写法:

    1. if (someConditions) {
    2. DoSomething();
    3. ...
    4. } else { // Good: else 与 if 在不同行
    5. ...
    6. }

    下面是不符合规范的案例:

    1. if (someConditions) { ... } else { ... } // Bad: else 与 if 在同一行

    循环语句

    规则3.7.1 循环语句要求使用大括号

    和if语句类似,我们要求for/while循环语句必须加上的大括号,即使循环体是空的,或者循环语句只有一条。

    1. for (int i = 0; i < someRange; i++) {
    2. DoSomething();
    3. }

    如果循环体是空的,应该使用空的大括号,而不是使用单个分号。 单个分号容易被遗漏,也容易被误认为是循环语句中的一部分。

    1. for (int i = 0; i < someRange; i++) { } // Good: for循环体是空,使用大括号,而不是使用分号
    2. while (someCondition) { } // Good:while循环体是空,使用大括号,而不是使用分号
    3. while (someCondition) {
    4. continue; // Good:continue表示空逻辑,可以使用大括号也可以不使用
    5. }

    坏的例子:

    1. for (int i = 0; i < someRange; i++) ; // Bad: for循环体是空,也不要只使用分号,要使用大括号
    2. while (someCondition) ; // Bad:使用分号容易让人误解是while语句中的一部分

    switch语句

    规则3.8.1 switch 语句的 case/default 要缩进一层

    switch 语句的缩进风格如下:

    1. switch (var) {
    2. case 0: // Good: 缩进
    3. DoSomething1(); // Good: 缩进
    4. break;
    5. case 1: { // Good: 带大括号格式
    6. DoSomething2();
    7. break;
    8. }
    9. default:
    10. break;
    11. }
    1. switch (var) {
    2. case 0: // Bad: case 未缩进
    3. DoSomething();
    4. break;
    5. default: // Bad: default 未缩进
    6. break;
    7. }

    表达式

    建议3.9.1 表达式换行要保持换行的一致性,运算符放行末

    较长的表达式,不满足行宽要求的时候,需要在适当的地方换行。一般在较低优先级运算符或连接符后面截断,运算符或连接符放在行末。运算符、连接符放在行末,表示“未结束,后续还有”。例:

    // 假设下面第一行已经不满足行宽要求

    1. if (currentValue > threshold && // Good:换行后,逻辑操作符放在行尾
    2. someConditionsion) {
    3. DoSomething();
    4. ...
    5. }
    6. int result = reallyReallyLongVariableName1 + // Good
    7. reallyReallyLongVariableName2;

    表达式换行后,注意保持合理对齐,或者4空格缩进。参考下面例子

    1. int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 +
    2. longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 4空格缩进
    3. int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 +
    4. longVaribleName4 + longVaribleName5 + longVaribleName6; // Good: 保持对齐

    变量赋值

    规则3.10.1 多个变量定义和赋值语句不允许写在一行

    每行只有一个变量初始化的语句,更容易阅读和理解。

    1. int maxCount = 10;
    2. bool isCompleted = false;

    下面是不符合规范的示例:

    1. int maxCount = 10; bool isCompleted = false; // Bad:多个变量初始化需要分开放在多行,每行一个变量初始化
    2. int x, y = 0; // Bad:多个变量定义需要分行,每行一个
    3. int pointX;
    4. int pointY;
    5. ...
    6. pointX = 1; pointY = 2; // Bad:多个变量赋值语句放同一行

    例外:for 循环头、if 初始化语句(C++17)、结构化绑定语句(C++17)中可以声明和初始化多个变量。这些语句中的多个变量声明有较强关联,如果强行分成多行会带来作用域不一致,声明和初始化割裂等问题。

    初始化

    初始化包括结构体、联合体、及数组的初始化

    规则3.11.1 初始化换行时要有缩进,并进行合理对齐

    结构体或数组初始化时,如果换行应保持4空格缩进。从可读性角度出发,选择换行点和对齐位置。

    1. const int rank[] = {
    2. 16, 16, 16, 16, 32, 32, 32, 32,
    3. 64, 64, 64, 64, 32, 32, 32, 32
    4. };

    指针与引用

    建议3.12.1 指针类型"*"跟随变量名或者类型,不要两边都留有或者都没有空格

    指针命名: *靠左靠右都可以,但是不要两边都有或者都没有空格。

    1. int* p = NULL; // Good
    2. int *p = NULL; // Good
    3. int*p = NULL; // Bad
    4. int * p = NULL; // Bad

    例外:当变量被 const 修饰时,"*" 无法跟随变量,此时也不要跟随类型。

    1. char * const VERSION = "V100";

    建议3.12.2 引用类型"&"跟随变量名或者类型,不要两边都留有或者都没有空格

    引用命名:&靠左靠右都可以,但是不要两边都有或者都没有空格。

    1. int i = 8;
    2. int& p = i; // Good
    3. int &p = i; // Good
    4. int & p = i; // Bad
    5. int&p = i; // Bad

    编译预处理

    规则3.13.1 编译预处理的"#"统一放在行首,嵌套编译预处理语句时,"#"不缩进

    编译预处理的"#"统一放在行首,即使编译预处理的代码是嵌入在函数体中的,"#"也应该放在行首。

    1. #if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // Good:"#"放在行首
    2. #define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good:"#"放在行首
    3. #else
    4. #define ATOMIC_X86_HAS_CMPXCHG16B 0
    5. #endif
    6. int FunctionName() {
    7. if (someThingError) {
    8. ...
    9. #ifdef HAS_SYSLOG // Good:即便在函数内部,"#"也放在行首
    10. WriteToSysLog();
    11. #else
    12. WriteToFileLog();
    13. #endif
    14. }
    15. }

    内嵌的预处理语句"#"不缩进

    1. #if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
    2. #define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good:区分层次,便于阅读
    3. #else
    4. #define ATOMIC_X86_HAS_CMPXCHG16B 0
    5. #endif

    空格和空行

    建议3.14.1 水平空格应该突出关键字和重要信息,避免不必要的留白

    水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。总体规则如下:

    • if, switch, case, do, while, for等关键字之后加空格;
    • 小括号内部的两侧,不要加空格;
    • 大括号内部两侧有无空格,左右必须保持一致;
    • 一元操作符(& * + ‐ ~ !)之后不要加空格;
    • 二元操作符(= + ‐ < > * / % | & ^ <= >= == != )左右两侧加空格
    • 三目运算符(? :)符号两侧均需要空格
    • 前置和后置的自增、自减(++ —)和变量之间不加空格
    • 结构体成员操作符(. ->)前后不加空格
    • 逗号(,)前面不加空格,后面增加空格
    • 对于模板和类型转换(<>)和类型之间不要添加空格
    • 域操作符(::)前后不要添加空格
    • 冒号(:)前后根据情况来判断是否要添加空格常规情况:
    1. void Foo(int b) { // Good:大括号前应该留空格
    2. int i = 0; // Good:变量初始化时,=前后应该有空格,分号前面不要留空格
    3. int buf[kBufSize] = {0}; // Good:大括号内两侧都无空格

    函数定义和函数调用:

    1. int result = Foo(arg1,arg2);
    2. ^ // Bad: 逗号后面需要增加空格
    3. int result = Foo( arg1, arg2 );
    4. ^ ^ // Bad: 函数参数列表的左括号后面不应该有空格,右括号前面不应该有空格

    指针和取地址

    1. x = *p; // Good:*操作符和指针p之间不加空格
    2. p = &x; // Good:&操作符和变量x之间不加空格
    3. x = r.y; // Good:通过.访问成员变量时不加空格
    4. x = r->y; // Good:通过->访问成员变量时不加空格

    操作符:

    1. x = 0 // Good:赋值操作的=前后都要加空格
    2. x = -5 // Good:负数的符号和数值之前不要加空格
    3. ++x // Good:前置和后置的++/--和变量之间不要加空格
    4. x--;
    5. if (x && !y) // Good:布尔操作符前后要加上空格,!操作和变量之间不要空格
    6. v = w * x + y / z; // Good:二元操作符前后要加空格
    7. v = w * (x + z); // Good:括号内的表达式前后不需要加空格
    8. int a = (x < y) ? x : y; // Good: 三目运算符, ?和:前后需要添加空格

    循环和条件语句:

    1. if (condition) { // Good:if关键字和括号之间加空格,括号内条件语句前后不加空格
    2. ...
    3. } else { // Good:else关键字和大括号之间加空格
    4. ...
    5. }
    6. while (condition) {} // Good:while关键字和括号之间加空格,括号内条件语句前后不加空格
    7. for (int i = 0; i < someRange; ++i) { // Good:for关键字和括号之间加空格,分号之后加空格
    8. ...
    9. }
    10. switch (condition) { // Good: switch 关键字后面有1空格
    11. case 0: // Good:case语句条件和冒号之间不加空格
    12. ...
    13. break;
    14. ...
    15. default:
    16. ...
    17. break;
    18. }

    模板和转换

    1. // 尖括号(< and >) 不与空格紧邻, < 前没有空格, > 和 ( 之间也没有.
    2. vector<string> x;
    3. y = static_cast<char*>(x);
    4. // 在类型与指针操作符之间留空格也可以, 但要保持一致.
    5. vector<char *> x;

    域操作符

    1. std::cout; // Good: 命名空间访问,不要留空格
    2. int MyClass::GetValue() const {} // Good: 对于成员函数定义,不要留空格

    冒号

    1. // 添加空格的场景
    2. // Good: 类的派生需要留有空格
    3. class Sub : public Base {
    4. };
    5. // 构造函数初始化列表需要留有空格
    6. MyClass::MyClass(int var) : someVar(var) {
    7. DoSomething();
    8. }
    9. // 位域表示也留有空格
    10. struct XX {
    11. char a : 4;
    12. char b : 5;
    13. char c : 4;
    14. };
    1. // 不添加空格的场景
    2. // Good: 对于public:, private:这种类访问权限的冒号不用添加空格
    3. class MyClass {
    4. public:
    5. MyClass(int var);
    6. private:
    7. int someVar;
    8. };
    9. // 对于switch-case的case和default后面的冒号不用添加空格
    10. switch (value) {
    11. case 1:
    12. DoSomething();
    13. break;
    14. default:
    15. break;
    16. }

    注意:当前的集成开发环境(IDE)可以设置删除行尾的空格,请正确配置。

    建议3.14.2 合理安排空行,保持代码紧凑

    减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则:

    • 根据上下内容的相关程度,合理安排空行;
    • 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行
    • 不使用连续 3 个空行,或更多
    • 大括号内的代码块行首之前和行尾之后不要加空行。
    1. int Foo() {
    2. ...
    3. }
    4. // Bad:两个函数定义间超过了一个空行
    5. int Bar() {
    6. ...
    7. }
    8. if (...) {
    9. // Bad:大括号内的代码块行首不要加入空行
    10. ...
    11. // Bad:大括号内的代码块行尾不要加入空行
    12. }
    13. int Foo(...) {
    14. // Bad:函数体内行首不要加空行
    15. ...
    16. }

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

    1. class MyClass : public BaseClass {
    2. public: // 注意没有缩进
    3. MyClass(); // 标准的4空格缩进
    4. explicit MyClass(int var);
    5. ~MyClass() {}
    6. void SomeFunction();
    7. void SomeFunctionThatDoesNothing() {
    8. }
    9. void SetVar(int var) { someVar = var; }
    10. int GetVar() const { return someVar; }
    11. private:
    12. bool SomeInternalFunction();
    13. int someVar;
    14. int someOtherVar;
    15. };

    在各个部分中,建议将类似的声明放在一起, 并且建议以如下的顺序: 类型 (包括 typedef, using 和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它成员函数, 数据成员。

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

    1. // 如果所有变量能放在同一行:
    2. MyClass::MyClass(int var) : someVar(var) {
    3. DoSomething();
    4. }
    5. // 如果不能放在同一行,
    6. // 必须置于冒号后, 并缩进4个空格
    7. MyClass::MyClass(int var)
    8. : someVar(var), someOtherVar(var + 1) { // Good: 逗号后面留有空格
    9. DoSomething();
    10. }
    11. // 如果初始化列表需要置于多行, 需要逐行对齐
    12. MyClass::MyClass(int var)
    13. : someVar(var), // 缩进4个空格
    14. someOtherVar(var + 1) {
    15. DoSomething();
    16. }