Google C++ Style Guide

头文件

通常,每个.cc文件都应该有一个与之相关联的.h文件。但是,也有一些常见的例外,比如只包含main()函数的单元测试或小.cc文件。

独立的头文件

头文件应该是独立的(自己能够编译成功),并且以.h作为扩展名。用于被其他文件包含的,但并不是头文件应以.inc结尾并谨慎使用。

所有的头文件都应该是独立的。使用者和重构工具不应该遵循特殊的条件来包含头文件。具体来讲,就是头文件应该有保护机制(避免被多次#include,译者注)并且include所需要的其他所有头文件。

推荐将模板和内联函数的定义放置在其声明的文件中。使用这些结构的.cc文件都必须将它们的定义包含进来,否则在某些build配置下,链接过程可能会报错。

当为所有相关模板形参集合显式实例化的模板或是一个类的私有实现的模板时,允许将该模板定义在实例化这个模板的.cc文件中,但这个文件只能有一个并且只能是.cc文件。

在极少数情况下,设计的文件不是独立的。这些文件通常会在不寻常的位置被include,比如在另一个文件的中间(通常include文件都是在头部,译者注)。在这种情况下,这些文件不应该使用保护机制,也不应该包含它们所必须的其他头文件。这些文件的扩展名通常为.inc,以与正常的头文件相区分。这种形式的头文件应该谨慎使用,并尽可能使用独立的头文件。

#define保护

所有的头文件都应该使用#define保护机制来避免该头文件被多次包含(include)。符号名的格式应该为<PROJECT>_<PATH>_<FILE>_H_

为了保证符号名的唯一性,它们的名字中应该包含该文件在工程中的相对路径。例如,工程foo中的文件foo/src/bar/baz.h应该使用如下的包含机制:

#ifndef FOO_SRC_BAR_BAZ_H_
#define FOO_SRC_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_

译者注:原文中为FOO_BAR_BAZ_H_,但根据文中的讲述,正确的应该是FOO_SRC_BAR_BAZ_H_

转发声明

当可能的话,避免使用转发声明。仅使用#include来包含你需要的都文件。

定义

转发声明是某个类、函数或模板的声明没有与之相关的定义。

优点

  • 转发声明可以节省编译时间,因为#include强制编译器打开更多的文件并除了更多的输入。
  • 转发声明可以节省不必要的重复编译。因为头文件中不相关的改动,#include会强制你的代码重复编译更多次。

缺点

  • 转发声明会隐藏依赖关系,允许用户代码在头文件发生更改时跳过必要的重新编译。
  • 库的更改可能会破坏转发声明。函数和模板的转发声明可能会阻止头文件所有者对其API的兼容性更改,例如扩展参数类型,添加具有默认值的模板参数或者转移到新的命名空间。
  • 来自std::命名空间的转发符号声明会产生未定义的行为。
  • 可能很难确定是否需要转发声明或者需要完整的#include。用转发声明替换#include可能会悄悄地改变代码的含义。
// b.h:
struct B {};
struct D : B {};

// good _user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); } // calls f(B*)

如果用B和D的转发声明替换#include,那么test()将调用f(void*)

  • 从一个头文件声明多个符号的转发声明可能比使用#include一个头文件更加冗长。
  • 使用转发声明的结构代码(比如,使用指针成员而不是对象成员)可能会使代码更慢更复杂。

选择方法

尽量避免对在另一个项目中定义的实体使用前向声明。
当使用头文件中声明的函数时,总是#include包含那个头文件。
在使用类模板时,首选使用#include包含其头文件。

更多关于何时使用#include来包含一个头文件的规则,请参见名字和包含顺序。

内联函数

只有当函数足够短(10 行或更少)时才将该函数定义为内联函数。

定义

您可以通过允许编译器以内联方式扩展它们的方式来声明函数,而不是通过通常的函数调用机制来调用它们。

 

优点

只要内联函数很小,内联函数就可以生成更高效的目标代码。请随意使用inline accessor和mutator,以及其他短的并对性能要求很高函数。

 

缺点

内联过度使用会使程序变慢。根据函数的大小,内联可能导致代码大小增加或减少。内联一个非常小的访问函数通常会减少代码的大小,而内联一个非常大的函数会显著增加代码的大小。在现代处理器上,较小的代码通常由于能够更好地使用指令缓存而运行得更快。

 

选择方法

一个通用的经验是,如果超过10行,则不内联函数。注意析构函数,由于隐式成员和基本析构函数的调用,这些析构函数往往比它们显示的要长。

另一个有用的经验是:将使用循环语句或switch语句的函数定义为内联函数通常是不划算的(除非在大多数情况下循环语句或切换语句从不执行)。

需要注意的是,即使一些函数被声明成了inline,但这些函数并不总是内联的。例如,虚函数和递归函数通常不会内联。通常递归函数不应该内联。内联虚函数的主要原因是将其定义放在类中,以方便或记录其行为,例如accessors 和mutators。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

16 − 2 =

74 − 71 =