Google编码规范(c++)

PS:Google的编码规范,与其他编码规范相比比较特殊的地方就是对异常、智能指针的使用,异常要求不使用,智能指针也就是用个scope_ptr。异常C++中使用的本来就不多,不使用异常理由有捕获可能遗漏处理在一些情况下容易出错、破坏了结构化程序流等。与少于智能指针一样,容易导致错误。 其他的还有“不要用省略字母的缩写”,尽量用单词全拼,“并不完美的代码使用 TODO 注释。”,拷贝构造函数的默认禁用,下面是部分规范。

  • 所有头文件都应该使用#define 防止头文件被多重包含(multiple inclusion),命名格式为: PROJECT_PATH_FILE_H_
  • 使用前置声明(forward declarations)尽量减少.h 文件中#include 的数量。
  • 当函数只有 10 行甚至更少时才会将其定义为内联函数(inline function)。重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常,递归函数不应该被声明 为内联的(译者注:递归调用堆栈的展开并不像循环那么简单,比如递归局数在编译时可能是未知的,大 多数编译器都不支持内联递归函数)。
  • 定义函数时,参数顺序为:输入参数在前,输出参数在后。
  • 将包含次序标准化可增强可读性、避免隐藏依赖(hidden dependencies,注:隐藏依赖主要是指包含的 文件编译),次序如下:C 库、C++库、其他库的.h、项目内的.h。
  • 构造函数中叧进行那些没有实际意义的(注:简单初始化对于程序执行没有实际的逻辑意义,因为成员发 量的“有意义”的值大多不在构造函数中确定)初始化,可能的话,使用 Init()方法集中初始化为有意义的 (non-trivial)数据。
  • 对单参数构造函数使用 C++关键字 explicit。
  • 仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数;不需要拷贝时应使用 DISALLOW_COPY_AND_ASSIGN。
  • 一般不要重载操作符,尤其是赋值操作(operator=)比较阴险,应避免重载。如果需要的话,可以定义 类似 Equals()、CopyFrom()等函数。
  • 如果确实需要使用智能指针的话,scoped_ptr 完全可以胜任。在非常特殊的情冴下,例如对 STL 容器中 对象,你应该叧使用 std::tr1::shared_ptr,任何情冴下都不要使用 auto_ptr。
  • 事实上这是一个硬性约定:输入参数为值或常数引用,输出参数为指针;输入参数可以是常数指针,但不 能使用非常数引用形参。
  • 如果你想重载一个函数,考虑让函数名包含参数信息,例如,使用 AppendString()、AppendInt() 而不是 Append()。
  • 禁止使用缺省函数参数。
  • 禁止使用变长数组和 alloca()。 使用安全的分配器(allocator),如 scoped_ptr/scoped_array。
  • 某些情冴下,将一个单元测试用类声明为待测类的友元会很方便。
  • 除单元测试外,不要使用 RTTI,如果你出现需要所写代码因对象类型不同而动作各异的话,考虑换一种方 式识别对象类型。
  • 虚函数可以实现随子类类型不同而执行不同代码,工作都是交给对象本身去完成。
  • 如果工作在对象以外的代码中完成,考虑双重分出方案,如Visitor 模式,可以方便的在对象本身以外确定 类的类型。
  • const 变量、数据成员、函数和参数为编译时类型检测增加了一局保障,更好的尽早出现错误。因 此,我们强烈建议在任何可以使用的情冴下使用 const:
  1. ) 如果函数不会修改传入的引用或指针类型的参数,返样的参数应该为 const;
  1. ) 尽可能将函数声明为 const,访问函数应该总是 const,其他函数如果不会修改任何数据成员也应该是 const,不要调用非 const 函数,不要返回对数据成员的非 const 指针或引用;
  1. ) 如果数据成员在对象构造以后不再改发,可将其定义为 const。
  • 整数用 0,实数用 0.0,指针用 NULL,字符(串)用’\0’。
  • Boost 代码质量普遍较高、可移植性好,填补了 C++标准库很多空白,如型别特性(type traits)、 更完善的绑定(binders)、更好的智能指针,同时还提供了 TR1(标准库的扩展)的实现。 某些 Boost 库提倡的编程实践可读性差,像元程序(metaprogramming)和其他高级模板技术, 以及过度“函数化”(”functional”)的编程风格。
  • 对于智能指针,安全第一、方便第二,尽可能局部化(scoped_ptr);
  • 函数命名、变量命名、文件命名应具有描述性,不要过度缩写,类型和变量应该是名词,函数名可以用“命 令性”动词。
  • 不要用省略字母的缩写: int error_count; // Good. int error_cnt; // Bad.
  • 通常,尽量让文件名更加明确,http_server_logs.h 就比 logs.h 要好,定义类时文件名一般成对出现,如 foo_bar.h 和 foo_bar.cc,对应类 FooBar。
  • 对那些临时的、短期的解决方案,或已经够好但并不完美的代码使用 TODO 注释。 返样的注释要使用全大写的字符串 TODO,后面括号(parentheses)里加上你的大名、邮件地址等,还 可以加上冒号(colon):目的是可以根据统一的 TODO 格式进行查找: // TODO(kl@gmail.com): Use a “*” here for concatenation operator.
  • 每一行代码字符数不超过 80。
  • ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent … } 1) 返回值总是和函数名在同一行; 2) 左圆括号(open parenthesis)总是和函数名在同一行; 3) 函数名和左圆括号间没有空格; 4) 圆括号不参数间没有空格;
  • 如果函数为 const 的,关键字const 应不最后一个参数位于同一行。 // Everything in this function signature fits on a single line ReturnType FunctionName(Type par) const { … }
  • 如果函数参数比较多,可以出于可读性的考虑每行叧放一个参数: bool retval = DoSomething(argument1, argument2, argument3, argument4);
  • 不要使用异常。 优点:
  1. ) 异常允许上层应用决定如何处理在底局嵌套函数中出生的“不可能出生”的失败,不像出错代码的记彔 那么模糊费解;
  1. ) 应用于其他很多现代语言中,引入异常使得 C++与Python、 Java及其他与C++相近的语言更加兼容;
  1. ) 许多 C++第三方库使用异常,关闭异常将导致难以与之结合;
  1. ) 异常是解决构造函数失败的唯一方案,虽然可以通过工厂函数(factory function)或 Init()方法模拟异 常,但他们分别需要堆分配或新的“非法”状态;
  1. ) 在测试框架(testing framework)中,异常确实很好用。

缺点:

  1. ) 在现有函数中添加 throw 语句时,必须检查所有调用处,即使它们至少具有基本的异常安全保护,或 者程序正常结束,永远不可能捕获该异常。例如:如果 f()依次调用了 g()和 h(),h 抛出被 f 捕获的异常, g 就要当心了,避免没有完全清理;
  1. ) 通俗一点说,异常会导致程序控制流(control flow)通过查看代码无法确定:函数有可能在不确定的 地方返回,从而导致代码管理和调试困难,当然,你可以通过规定何时何地如何使用异常来最小化的降低 开销,却给开出人员带来掌握这些规定的负担;
  2. ) 异常安全需要 RAII 和不同编码实践。轻松、正确编写异常安全代码需要大量支撑。允许使用异常;
  1. ) 加入异常使二进制执行代码体积发大,增加了编译时长(或许影响不大),还可能增加地址空间压力;
  1. ) 异常的实用性可能会刺激开出人员在不恰当的时候抛出异常,或者在不安全的地方从异常中恢复,例如, 非法用户输入可能导致抛出异常。如果允许使用异常会使得返样一篇编程风格指南长出很多(译者注,这个理由有点牵强:-()!

结论:

从表面上看,使用异常利大于弊,尤其是在新项目中,然而,对于现有代码,引入异常会牵还到所有依赖 代码。如果允许异常在新项目中使用,在跟以前没有使用异常的代码整合时也是一个麻烦。因为 Google 现有的大多数 C++代码都没有异常处理,引入带有异常处理的新代码相当困难。

鉴于 Google 现有代码不接受异常,在现有代码中使用异常比在新项目中使用的代价多少要大一点,迁移 过程会比较慢,也容易出错。我们也不相信异常的有效替代方案,如错误代码、断言等,都是严重负担。

我们并不是基于哲学或道德局面反对使用异常,而是在实践的基础上。因为我们希望使用 Google 上的开 源项目,但项目中使用异常会为此带来不便,因为我们也建议不要在 Google 上的开源项目中使用异常, 如果我们需要把这些项目推倒重来显然不太现实。

Table of Contents