google c++ 代码规范关键条目总结

编程入门 行业动态 更新时间:2024-10-09 12:26:21

google c++ 代码规范关键<a href=https://www.elefans.com/category/jswz/34/1771400.html style=条目总结"/>

google c++ 代码规范关键条目总结

GOOGLE C++代码规范总结:

开始一个项目之前,首先应该明确编码规范。一个好的编码习惯可以让代码更易于阅读,同时也能让程序员避免犯一些常见的错误。

为了便于快速参考,此帖仅仅提取了关键点,具体的详解请参考官方地址:
英文:Google C++ Style Guide
中文:C++ 风格指南

Content

一、头文件
二、作用域
三、类
四、函数
五、来自GOOGLE的技巧
六、其他C++特性
七、命名约定
八、注释
九、格式
十、例外


一、头文件

  1. 通常每一个 文件都有一个对应的 .h 文件。
  2. 头文件应该能够自给自足(self-contained)。
  3. 所有头文件都应该使用 #define 来防止头文件被多重包含, 命名格式是<PROJECT>_<PATH>_<FILE>_H_
  4. 尽可能地避免使用前置声明。
  5. 只有当函数只有不大于 10 行时才将其定义为内联函数。
  6. 头文件包含顺序:
    1. 相关头文件 2. C 系统文件  3. C++ 系统文件  4. 其他库的 .h 文件  5. 本项目内 .h 文件  

例子:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_#include "foo/public/fooserver.h" // 优先位置#include <sys/types.h>
#include <unistd.h>#include <hash_map>
#include <vector>#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/public/bar.h"#endif // FOO_BAR_BAZ_H_

二、作用域

  1. 鼓励在 文件中使用匿名命名空间或 static 声明。
  2. 禁止使用 using 指示(using-directive)和内联命名空间(inline namespace)。
  3. 尽量不要用裸的全局函数,不要用类的静态方法模拟出命名空间的效果。
  4. 将函数变量尽可能置于最小作用域内(考虑效率的循环体是个例外), 并在变量声明时进行初始化。
  5. 禁止定义静态储存周期非POD变量。

例子:

#include "a.h"DEFINE_FLAG(bool, someflag, false, "dummy flag");namespace a {inline void my_inline_function() { // 左对齐,不要缩进// 限制在一个函数中的命名空间别名namespace baz = ::foo::bar::baz;...
}              } // namespace a

三、类

  1. 不要在构造函数中调用虚函数,也不要在无法报出错误时进行可能失败的初始化。
  2. 对于转换运算符和单参数构造函数, 使用explicit关键字。
  3. 如果你的类型需要,就让它们支持拷贝 / 移动。 否则,就把隐式产生的拷贝和移动函数禁用。
  4. 仅当只有数据成员时使用struct, 其它一概使用class
  5. 优先考虑组合。如果用继承的话,定义为public继承。
  6. 只在以下情况我们才允许多重继承: 最多只有一个基类是非抽象类;其它基类都是以 Interface 为后缀的 纯接口类。
  7. 除少数特定环境外,不要重载运算符。也不要创建用户定义字面量。
  8. 所有数据成员声明为private, 除非是static const类型成员。
  9. 类定义一般应以public:开始, 后跟protected:,最后是private:。每部分的内容按如下顺序:
    1. 类型 (包括 typedef, using 和嵌套的结构体与类)2. 常量3. 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它函数(从左向右)4. 数据成员
  1. 只有那些普通的, 或性能关键且短小的函数可以内联在类定义中。

四、函数

  1. 通常函数的参数顺序为: 输入参数在先, 后跟输出参数。
  2. 倾向于编写简短, 凝练的函数(不超过40行)。
  3. 所有引用参数都必须是const。输入参数是值参或const引用,输出参数为指针。输入参数可以是const指针, 但决不能是非const的引用参数, 除非特殊要求(如swap)。
  4. 函数重载要让人一目了然。比如用 AppendString() 和 AppendInt() 等代替重载多个 Append()。
  5. 只允许在非虚函数中使用缺省参数,且必须保证缺省参数的值始终一致。一般情况下建议使用函数重载。如果在每个调用点缺省参数的值都有可能不同, 缺省函数也不允许使用。
  6. 只有在常规写法 (返回类型前置) 不便于书写或不便于阅读时使用返回类型后置语法。

五、来自GOOGLE的技巧

  1. 动态分配出的对象最好有单一且固定的所有主, 并通过智能指针传递所有权。
  2. 使用cpplint.py检查风格错误。

六、其他C++特性

  1. 只在定义移动构造函数与移动赋值操作时使用右值引用. 不要使用 std::forward。
  2. 不允许使用变长数组和 alloca()。
  3. 通常友元应该定义在同一文件内, 避免代码读者跑到其它文件查找使用该私有成员的类。
  4. 不使用 C++ 异常。
  5. 禁止使用 RTTI。
  6. 不要使用 C 风格类型转换,而应该使用 C++ 风格。
  7. 只在记录日志时使用流。
  8. 对于迭代器和其他模板对象使用前缀形式 (++i) 的自增,自减运算符。
  9. 在任何可能的情况下都要使用 const。对于int const *foo 和 const int* foo,提倡但不强制 const 在前,但要保持代码的一致性。
  10. 在 C++11 里,用 constexpr 来定义真正的常量,或实现常量初始化。
  11. C++ 内建整型中, 仅使用 int。如果需要,使用<stdint.h> 中长度精确的整型。
  12. 使用断言来指出变量为非负数, 而不是使用无符号型。
  13. 代码应该对 64 位和 32 位系统友好。参考
  14. 创建 64 位常量时使用 LL 或 ULL 作为后缀。
  15. 使用宏时要非常谨慎, 尽量以内联函数, 枚举和常量代替之。
  16. 整数用 0, 实数用 0.0, 指针用 nullptr 或 NULL, 字符 (串) 用 ‘\0’。
  17. 尽可能用 sizeof(varname) 代替 sizeof(type)。
  18. 用 auto 绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
  19. 可以用列表初始化。
  20. 适当使用 lambda 表达式,所有捕获都要显式写出来。
  21. 不要使用复杂的模板编程。
  22. 只使用 Boost 中被认可的库。参考
  23. 适当用 C++11。

七、命名约定

  1. 命名要有描述性,少用缩写(广为人知的缩写是允许的)。
  2. 代码文件名要全部小写,可以包含下划线 “_” 或连字符 “-”,依照项目的约定. 如果没有约定, 那么 “_” 更好。不要使用已经存在于/usr/include下的文件名。
  3. C++ 文件要以 结尾,头文件以 .h 结尾。专门插入文本的文件则以 .inc 结尾。
  4. 类型名称的每个单词首字母均大写,不包含下划线:MyExcitingClass,MyExcitingEnum。
  5. 变量 (包括函数参数) 和数据成员名一律小写,单词之间用下划线连接。类的成员变量以下划线结尾,但结构体的就不用。
  6. 声明为 constexpr 或 const 的变量,或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合,如kDaysInAWeek
  7. 常规函数使用大小写混合,取值和设值函数则要求与变量名匹配:MyExcitingFunction(),MyExcitingMethod(),my_exciting_member_variable(),set_my_exciting_member_variable()。
  8. 命名空间以小写字母命名. 最高级命名空间的名字取决于项目名称。
  9. 枚举的命名应当和 常量(优先) 或 宏 一致: kEnumName 或是 ENUM_NAME。
  10. 通常不应该使用宏。若要用,用大写加下划线。如MY_MACRO。
  11. 如果你命名的实体与已有 C/C++ 实体相似, 可参考现有命名策略。

八、注释

  1. 使用 // 或 /* */,统一就好。
  2. 在每一个文件开头加入 版权公告 和 文件内容 信息。
  3. 每个类的定义都要附带一份注释, 描述类的功能和用法。
// Iterates over the contents of a GargantuanTable.
// Example:
//    GargantuanTableIterator* iter = table->NewIterator();
//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
//      process(iter->key(), iter->value());
//    }
//    delete iter;
class GargantuanTableIterator {...
};
  1. 类的声明和定义分开了(例如分别放在了 .h 和 文件中),此时,描述类用法的注释应当和接口定义放在一起,描述类的操作和实现的注释应当和实现放在一起。不要在 .h 和 之间复制注释。
  2. 函数声明处的注释描述函数功能,定义处的注释描述函数实现。注释使用叙述式 (“Opens the file”) 而非指令式(“Open the file”)。要叙述的内容如下(也要避免啰嗦,显而易见的就不必指明了):
  函数的输入输出.对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.函数是否分配了必须由调用者释放的空间.参数是否可以为空指针.是否存在函数使用上的性能隐患.如果函数是可重入的, 其同步前提是什么?
// Returns an iterator for this table.  It is the client's
// responsibility to delete the iterator when it is done with it,
// and it must not use the iterator once the GargantuanTable object
// on which the iterator was created has been deleted.
//
// The iterator is initially positioned at the beginning of the table.
//
// This method is equivalent to:
//    Iterator* iter = table->NewIterator();
//    iter->Seek("");
//    return iter;
// If you are going to immediately seek to another place in the
// returned iterator, it will be faster to use NewIterator()
// and avoid the extra seek.
Iterator* GetIterator() const;
  1. 函数定义处的注释重点要放在如何实现上。
  2. 如果变量的变量名和类型名不足以说明,就应该用注释说明其用途。
  3. 在巧妙或者晦涩的代码段前要加代码前注释:
// Divide result by two, taking into account that x
// contains the carry from the add.
for (int i = 0; i < result->size(); i++) {x = (x << 8) + (*result)[i];(*result)[i] = x >> 1;x &= 1;
}
  1. 在巧妙或者晦涩的代码行后要加行注释,在行尾空两格进行注释或多行时注意对齐:
DoSomething();                  // Comment here so the comments line up.
DoSomethingElseThatIsLonger();  // Two spaces between the code and the comment.
{ // One space before comment when opening a new scope is allowed,// thus the comment lines up with the following comments and code.DoSomethingElse();  // Two spaces before line comments normally.
}
std::vector<string> list{// Comments in braced lists describe the next element..."First item",// .. and should be aligned appropriately.
"Second item"};
DoSomething(); /* For trailing block comments, one space is fine. */
  1. 对于函数参数,用具名变量代替大段而复杂的嵌套表达式。
  2. 不要描述显而易见的现象,不要用自然语言翻译代码作为注释。
  3. 通常是完整的带结束标点的叙述语句,语法及标点风格要统一。
  4. 对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 TODO 注释。
// TODO(kl@gmail): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature
  1. 通过弃用注释(DEPRECATED)以标记某接口点已弃用。

九、格式

  1. 每一行代码字符数尽量不超过80。
  2. 尽量不使用非 ASCII 字符,使用时必须使用 UTF-8 编码。
  3. 不应将用户界面的文本硬编码到源代码中。
  4. 缩进只使用空格,每次2个空格。不要在代码中使用制表符。
  5. 返回类型和函数名在同一行,参数也尽量放在同一行,如果放不下就对形参分行,分行方式与函数调用一致,其余细节如下:
// 右圆括号和函数名、第一个参数名间没有空格。
// 返回类型如果与函数名分行的话不缩进。
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(Type par_name1,  // 4 space indentType par_name2,Type par_name3) { // 右圆括号和左大括号间有一个空格且在同一行DoSomething();  // 2 space indent...
} // 单独一行// 未被使用的参数如果其用途不明显的话, 在函数定义处将参数名注释起来
void Circle::Rotate(double /*radians*/) {} // 属性,和展开为属性的宏,写在函数声明或定义的最前面
MUST_USE_RESULT bool IsOK();
  1. lambda表达式格式和函数一样:
int x = 0;
auto add_to_x = [&x](int n) { x += n; };std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {return blacklist.find(i) != blacklist.end();}),digits.end());
  1. 函数调用格式:
// 在同一行
bool retval = DoSomething(argument1, argument2, argument3);// 对参数分行
bool retval = DoSomething(averyveryveryverylongargument1,argument2, argument3);if (...) {......if (...) {// 缩进 4 空格对参数分行DoSomething(argument1, argument2,  // 4 空格缩进argument3, argument4);}// 复杂表达式参数,以具名对象方式传入
int my_heuristic = scores[x] * y + bases[x];
bool retval = DoSomething(my_heuristic, x, y, z);// 也可以直接用,加上注释
bool retval = DoSomething(scores[x] * y + bases[x],  // Score heuristic.x, y, z);// 如果参数有格式,也可以使用参数本身的格式
my_widget.Transform(x1, x2, x3,y1, y2, y3,z1, z2, z3);
  1. 列表初始化格式:
// 一行列表初始化示范.
return {foo, bar};
functioncall({foo, bar});
pair<int, int> p{foo, bar};// 当不得不断行时.
SomeFunction({"assume a zero-length name before {"},  // 假设在 { 前有长度为零的名字.some_other_function_parameter);
SomeType variable{some, other, values,{"assume a zero-length name before {"},  // 假设在 { 前有长度为零的名字.SomeOtherType{"Very long string requiring the surrounding breaks.",  // 非常长的字符串, 前后都需要断行.some, other values},SomeOtherType{"Slightly shorter string",  // 稍短的字符串.some, other, values}};
SomeType variable{"This is too long to fit all in one line"};  // 字符串过长, 因此无法放在同一行.
MyType m = {  // 注意了, 您可以在 { 前断行.superlongvariablename1,superlongvariablename2,{short, interior, list},{interiorwrappinglist,interiorwrappinglist2}};
  1. 条件语句格式,重要的是坚持一种并始终保持下去:
if (condition) {  // if后有空格,圆括号里没有空格....  // 2 空格缩进.
} else if (...) {  // else 与 if 的右括号同一行....
} else {...
}if (x == kFoo) return new Foo(); // 简短的情况,可以不用大括号if (condition)DoSomething();  // 2 空格缩进.// 只要其中一个分支用了大括号, 两个分支都要用上大括号.
if (condition) {foo;
} else {bar;
}
  1. 循环语句和switch语句:
switch (var) {case 0: {  // 2 空格缩进...      // 4 空格缩进break;}case 1: {  //大括号可以用可以不用...break;}default: {assert(false);  // 如果永远执行不到这里,就加一条assert}
}// 循环体单行语句可以不用大括号
for (int i = 0; i < kSomeNumber; ++i)printf("I love you\n");// 也可以用大括号
for (int i = 0; i < kSomeNumber; ++i) {printf("I take it back\n");
}while (condition) {// 反复循环直到条件失效.
}
for (int i = 0; i < kSomeNumber; ++i) {}  // 好 - 空循环体.
while (condition) continue;  // 好 - contunue 表明没有逻辑.
while (condition);  // 差 - 看起来仅仅只是 while/loop 的部分之一.
  1. ".“或”->"前后不要有空格. 指针/取地址操作符 (*, &) 之后不能有空格。
x = *p;
p = &x;
x = r.y;
x = r->y;// 好, 空格前置.
char *c;
const string &str;// 好, 空格后置.
char* c;
const string& str;
int x, *y;  // 不允许 - 在多重声明中不能使用 & 或 *
char * c;  // 差 - * 两边都有空格
const string & str;  // 差 - & 两边都有空格.
  1. 布尔表达式格式:
if (this_one_thing > this_other_thing &&a_third_thing == a_fourth_thing &&yet_another && last_one) {...
}
  1. return语句格式:
return result;                  // 返回值很简单, 没有圆括号.// 可以用圆括号把复杂表达式圈起来, 改善可读性.
return (some_long_condition &&another_condition);return (value);                // 差 - 毕竟您从来不会写 var = (value);
return(result);                // 差 - return 可不是函数!
  1. 变量初始化:
// 以下都可以
int x = 3;
int x(3);
int x{3};
string name("Some Name");
string name = "Some Name";
string name{"Some Name"};
  1. 预处理指令从行首开始:
  if (lopsided_score) {
#if DISASTER_PENDING      // 正确 - 从行首开始DropEverything();
# if NOTIFY               // 非必要 - # 后跟空格NotifyClient();
# endif
#endifBackToNormal();}
  1. 访问控制块的声明依次序是 public:, protected:, private:, 每个都缩进 1 个空格:
class MyClass : public OtherClass {public:      // 注意有一个空格的缩进MyClass();  // 标准的两空格缩进explicit MyClass(int var);~MyClass() {}void SomeFunction();void SomeFunctionThatDoesNothing() {}void set_some_var(int var) { some_var_ = var; }int some_var() const { return some_var_; }private:bool SomeInternalFunction();int some_var_;int some_other_var_;
};
  1. 构造函数初始化列表:
// 如果所有变量能放在同一行:
MyClass::MyClass(int var) : some_var_(var) {DoSomething();
}// 如果不能放在同一行,
// 必须置于冒号后, 并缩进 4 个空格
MyClass::MyClass(int var): some_var_(var), some_other_var_(var + 1) {DoSomething();
}// 如果初始化列表需要置于多行, 将每一个成员放在单独的一行
// 并逐行对齐
MyClass::MyClass(int var): some_var_(var),             // 4 space indentsome_other_var_(var + 1) {  // lined upDoSomething();
}// 右大括号 } 可以和左大括号 { 放在同一行
// 如果这样做合适的话
MyClass::MyClass(int var): some_var_(var) {}
  1. 命名空间内容不缩进。声明嵌套命名空间时,每个命名空间都独立成行。
  2. 行尾不要加多余的空格。
  3. 操作符:
// 赋值运算符前后总是有空格.
x = 0;// 其它二元操作符也前后恒有空格, 不过对于表达式的子式可以不加空格.
// 圆括号内部没有紧邻空格.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);// 在参数和一元操作符之间不加空格.
x = -5;
++x;
if (x && !y)...
  1. 不在万不得已,不要使用空行。两函数之间的空行不要超过两行。

十、例外

  1. 已有既定风格的项目。
  2. Windows代码。
2020/05/07 22:57

更多推荐

google c++ 代码规范关键条目总结

本文发布于:2024-02-25 12:48:28,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1699024.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:条目   关键   代码   google

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!