Google C++ 編程規範

Google C++ 编程规范

一、头文件

1. #define 的保护

2. 头文件依赖

3. 内联函数

4. -inl.h 文件

5. 函数参数顺序(Function Parameter Ordering)

6. 包含文件的名称及次序

二、作用域

1. 命名空间(Namespaces)

2. 嵌套类(Nested Class)

3. 非成员函数( Nonmember)、静态成员函数( Static Member)和全局函数(Global Functions)

4. 局部变量(Local Variables)

5. 全局变量(Global Variables)

三、类

1. 构造函数(Constructor)的职责

2. 默认构造函数(Default Constructors)

3. 明确的构造函数(Explicit Constructors)

4. 拷贝构造函数(Copy Constructors)

5. 结构体和类(Structs vs. Classes)

6. 继承(Inheritance)

7. 多重继承(Multiple Inheritance)

8. 接口(Interface)

9. 操作符重载(Operator Overloading)

10. 存取控制(Access Control)

11. 声明次序(Declaration Order)

12. 编写短小函数(Write Short Functions)

四、Google 特有的风情

1. 智能指针(Smart Pointers)

五、其他C++特性

1. 引用参数(Reference Arguments)

2. 函数重载(Function Overloading)

3. 缺省参数(Default Arguments)

4. 变长数组和alloca(Variable-Length Arrays and alloca())

5. 友元(Friends)

6. 异常(Exceptions)

7. 运行时类型识别(Run-Time Type Information, RTTI)

8. 类型转换(Casting)

9. 流(Streams)

10. 前置自增和自减(Preincrement and Predecrement)

11. const 的使用(Use of const)

12. 整型(Integer Types)

13. 64 位下的可移植性(64-bit Portability)

14. 预处理宏(Preprocessor Macros)

15. 0 和 NULL(0 and NULL)

16. sizeof(sizeof)

17. Boost 库(Boost)

六、命名约定

1. 通用命名规则(General Naming Rules)

2. 文件命名(File Names)

3. 类型命名(Type Names)

4. 变量命名(Variable Names)

5. 常量命名(Constant Names)

6. 函数命名(Function Names)

7. 命名空间(Namespace Names)

8. 枚举命名(Enumerator Names)

9. 宏命名(Macro Names)

10. 命名规则例外(Exceptions to Naming Rules)

七、注释

1. 注释风格(Comment Style)

2. 文件注释(File Comments)

3. 类注释(Class Comments)

4. 函数注释(Function Comments)

5. 变量注释(Variable Comments)

6. 实现注释(Implementation Comments)

7. 标点、拼写和语法(Punctuation, Spelling and Grammar)

8. TODO 注释(TODO Comments)

八、格式

1. 行长度(Line Length)

2. 非 ASCII 字符(Non-ASCII Characters)

3. 空格还是制表位(Spaces vs. Tabs)

4. 函数声明与定义(Function Declarations and Definitions)

5. 函数调用(Function Calls)

6. 条件语句(Conditionals)

7. 循环和开关选择语句(Loops and Switch Statements)

8. 指针和引用表达式(Pointers and Reference Expressions)

9. 布尔表达式(Boolean Expressions)

10. 函数返回值(Return Values)

11. 变量及数组初始化(Variable and Array Initialization)

12. 预处理指令(Preprocessor Directives)

13. 类格式(Class Format)

14. 初始化列表(Initializer Lists)

15. 命名空间格式化(Namespace Formatting)

16. 水平留白(Horizontal Whitespace)

17. 垂直留白(Vertical Whitespace)

九、规则之例外

1. 现有不统一代码(Existing Non-conformant Code)

2. Windows 代码(Windows Code)

十、团队合作

一、头文件

通常,每一个.cc 文件(C++的源文件)都有一个对应的.h 文件(头文件),也有一些例

外,如单元测试代码和只包含main()的.cc 文件。

正确使用头文件可令代码在可读性、文件大小和性能上大为改观。

下面的规则将引导你规避使用头文件时的各种麻烦。

1. #define的保护

所有头文件都应该使用#define 防止头文件被多重包含(multiple inclusion),命名格式

当是:___H_

为保证唯一性,头文件的命名应基于其所在项目源代码树的全路径。例如,项目foo 中的头

文件foo/src/bar/baz.h 按如下方式保护:

#ifndef FOO_BAR_BAZ_H_

#define FOO_BAR_BAZ_H_

...

#endif // FOO_BAR_BAZ_H_

2. 头文件依赖

使用前置声明(forward declarations)尽量减少.h 文件中#include 的数量。

当一个头文件被包含的同时也引入了一项新的依赖(dependency),只要该头文件被修改,

代码就要重新编译。如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那

些包含了你的头文件的代码重新编译。因此,我们宁可尽量少包含头文件,尤其是那些包含

在其他头文件中的。

使用前置声明可以显著减少需要包含的头文件数量。举例说明:头文件中用到类File,但不

需要访问File 的声明,则头文件中只需前置声明class File;无需#include

"file/base/file.h"。

在头文件如何做到使用类Foo 而无需访问类的定义?

1) 将数据成员类型声明为Foo *或Foo &;

2) 参数、返回值类型为Foo 的函数只是声明(但不定义实现);

3) 静态数据成员的类型可以被声明为Foo,因为静态数据成员的定义在类定义之外。

另一方面,如果你的类是Foo 的子类,或者含有类型为Foo 的非静态数据成员,则必须为

之包含头文件。

有时,使用指针成员(pointer members,如果是scoped_ptr 更好)替代对象成员(object

members)的确更有意义。然而,这样的做法会降低代码可读性及执行效率。如果仅仅为

了少包含头文件,还是不要这样替代的好。

当然,.cc 文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。

译者注:能依赖声明的就不要依赖定义。

3. 内联函数

只有当函数只有10 行甚至更少时才会将其定义为内联函数(inline function)。

定义(Definition):当函数被声明为内联函数之后,编译器可能会将其内联展开,无需

按通常的函数调用机制调用内联函数。

优点:当函数体比较小的时候,内联该函数可以令目标代码更加高效。对于存取函数

(accessor、mutator)以及其他一些比较短的关键执行函数。

缺点:滥用内联将导致程序变慢,内联有可能是目标代码量或增或减,这取决于被内联的函

数的大小。内联较短小的存取函数通常会减少代码量,但内联一个很大的函数(译者注:如

果编译器允许的话)将戏剧性的增加代码量。在现代处理器上,由于更好的利用指令缓存

(instruction cache),小巧的代码往往执行更快。

结论:一个比较得当的处理规则是,不要内联超过10 行的函数。对于析构函数应慎重对待,

析构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被

调用!

另一有用的处理规则:内联那些包含循环或switch 语句的函数是得不偿失的,除非在大多

数情况下,这些循环或switch 语句从不执行。

重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常,递归函数

不应该被声明为内联的(译者注:递归调用堆栈的展开并不像循环那么简单,比如递归层数

在编译时可能是未知的,大多数编译器都不支持内联递归函数)。析构函数内联的主要原因

是其定义在类的定义中,为了方便抑或是对其行为给出文档。

4. -inl.h文件

复杂的内联函数的定义,应放在后缀名为-inl.h 的头文件中。

在头文件中给出内联函数的定义,可令编译器将其在调用处内联展开。然而,实现代码应完

全放到.cc 文件中,我们不希望.h 文件中出现太多实现代码,除非这样做在可读性和效率上

有明显优势。

如果内联函数的定义比较短小、逻辑比较简单,其实现代码可以放在.h 文件中。例如,存

取函数的实现理所当然都放在类定义中。出于实现和调用的方便,较复杂的内联函数也可以

放到.h 文件中,如果你觉得这样会使头文件显得笨重,还可以将其分离到单独的-inl.h 中。

这样即把实现和类定义分离开来,当需要时包含实现所在的-inl.h 即可。

-inl.h 文件还可用于函数模板的定义,从而使得模板定义可读性增强。

要提醒的一点是,-inl.h 和其他头文件一样,也需要#define 保护。

5. 函数参数顺序(Function Parameter Ordering)

定义函数时,参数顺序为:输入参数在前,输出参数在后。

C/C++函数参数分为输入参数和输出参数两种,有时输入参数也会输出(译者注:值被修

改时)。输入参数一般传值或常数引用(const references),输出参数或输入/输出参数

为非常数指针(non-const pointers)。对参数排序时,将所有输入参数置于输出参数之

前。不要仅仅因为是新添加的参数,就将其置于最后,而应该依然置于输出参数之前。

这一点并不是必须遵循的规则,输入/输出两用参数(通常是类/结构体变量)混在其中,会

使得规则难以遵循。

Google C++ 编程规范

Google C++ 编程规范

Google C++ 编程规范

文章已整理为PDF文档,想要获取该文档的可以私信我关键字+资料


分享到:


相關文章: