Loading...

Google C++ 编程风格指南(1):头文件(1)

Google C++ 编程风格指南(1):头文件(1)

  • #define 保护
  • 头文件依赖
  • 内联函数

通常来讲,每一个 .cc 文件都应该有一个与之关联的 .h 文件,不过有一些例外,比方说单元测试和小到只拥有一个 main() 函数的 .cc 文件。

正确使用头文件可以大大改善代码的可读性、长度和性能。

下面的准则将指引你跨越使用头文件时各种各样的陷阱。

#define 保护

所有的头文件都应该使用 #define 宏来防止被多次包含,书写宏的格式应该是 PROJECT_PATH_FILE_H_。

为了确保唯一性,#define 宏应该根据项目源文件树的完整路径书写。例如,项目 foo 中的 foo/src/bar.baz.h 应该使用下面的宏:

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

头文件依赖

使用前向声明来最小化头文件中 #include 的使用次数。

包含头文件来引入一个依赖,将会导致你的代码在头文件发生变化时需要重新编译。如果你的头文件包含了其它的一些头文件,当那些头文件发生任何改变后将会导致包含你的头文件的代码需要重新编译(绕口令吗?)。因此,我们建议最小化包含,尤其是在头文件中包含其它的头文件。

使用前向声明,你能够立杆见影地最小化你的头文件中需要包含的头文件的数量。举例来说,如果你的头文件使用了 File 类,但是不需要访问 File 类的定义(原文是 declaration,八成是笔误),那么你可以在你的头文件只对 File 类进行前向声明,而不是 #include "file/base/file.h"。

我们在什么情况下才能在头文件中使用 Foo 类而不需要 Foo 类的定义?

  • 声明数据成员的类型为 Foo* 或 Foo&。
  • 使用 Foo 作为函数的参数、(和/或)返回值。
  • 声明类型为 Foo 的静态数据成员,因为静态数据成员的定义在类定义之外。

与之相反,当你的类是 Foo 派生的子类或拥有一个类型为 Foo 的数据成员,必须在头文件中包含 Foo 的头文件。

有时使用指针成员比对象成员更有意义。尽管如此,这将削弱代码的可读性和性能,如果唯一的目的只是最小化头文件的包含数时,避免对象到指针的转换。

当然,.cc 文件一般必须要有它们所使用的类的定义,所以通常都会包含数个头文件。

内联函数

仅当函数很小,比方说只有 10 行或更少时,才将它们声明了内联。

内联函数的定义:将函数声明为内联将允许编译器在行内展开函数,而不是通过通常的函数调用机制对它们进行调用。

好处:只要这些函数很小,内联就能够产生更加高效的代码。放心地将 accessors 和 mutators 声明为内联吧,还有其它一些性能至上的函数。

坏处:滥用内联将会导致程序更慢。根据一个函数的大小,内联会导致代码的尺寸增大或减小。通常地,让一个非常小的 accessor 函数内联会减小代码的体积,然而让一个非常大的函数内联将会显著地增大代码的体积。现代的处理器通常运行小代码会更快,因为可以充分利用缓存。

如何决定是否使用内联
一个体面的准则是:当一个函数超过 10 行就不要内联。当心析构函数,它们往往比看上去更长,因为它们会隐式调用成员或基类的析构函数!

另一个有用的准则是:对拥有循环或 switch 语句的函数使用内联并不会使之更高效(除非循环或 switch 语句永远不会被执行)。

还有一点很重要:即使一个函数被声明为内联也不见得它就真的是内联。例如:虚函数和递归函数一般都不会内联。通常来讲,递归函数就不应该内联。使一个虚函数内联的主要原因是将它的定义放在类里面,不仅方便而且易于表示它的行为,比如 accessors 和 mutators。

0Comment(s). Blabla or Trackback

Blabla ↓

Connecting to server...

1Pingbacks & Trackbacks