第2部分。头文件
...
编写代码时,我们都使用代码格式化规则。有时会发明规则,而在其他情况下,则使用现成的样式指南。尽管所有C ++程序员都比本地人更容易阅读英语,但是在后者中拥有一本手册会更好。
本文是Google C ++样式指南的一部分翻译成俄语的内容。
原始文章(github上的fork),更新了翻译。
头文件
希望每个.cc源文件都有一个匹配的.h头文件。此规则也有一些例外,例如单元测试或仅包含main()函数的小型.cc文件。 头文件的正确使用会对代码的可读性,大小和性能产生巨大影响。 以下规则将帮助您避免头文件频繁出现问题。
独立头文件
头文件必须自给自足(就编译而言)并具有.h扩展名。打算包含在代码中的其他(非标头)文件必须具有.inc扩展名,并与包含代码配对。
所有头文件都应该是独立的。使用头文件时,用户和开发工具不应依赖特殊依赖项。头文件必须是可锁定的,并且必须包含所有必需的文件。
最好将模板和内联函数的定义及其声明放在同一文件中。这些定义必须包含在每个.cc中文件使用它们,否则某些构建配置上可能存在链接错误。如果声明和定义在不同的文件中,则其中一个应包括另一个。不要将定义分成单独的头文件(-inl.h)。以前,这种做法非常流行,现在是不可取的。
作为例外,如果从模板创建了模板参数的所有可用变体,或者模板仅实现了一个类使用的功能,则可以在一个(也只有一个).cc文件中定义该模板,并在其中使用该模板。
在极少数情况下,头文件不是自包含的。当文件包含在非标准位置(例如在另一个文件中间)时,可能会发生这种情况。在这种情况下,可能没有重新启用锁,并且也可能不包含其他头文件。使用.inc扩展名命名此类文件。成对使用,并尝试尽可能满足一般要求。
重新启动锁
必须对所有头文件进行#define保护,以防止重新包含。宏定义格式必须为:<PROJECT> _ <PATH> _ <FILE> _H_。
为确保唯一性,请使用项目树中的完整文件路径组件。例如,foo项目中的文件foo / src / bar / baz.h可能具有以下锁定:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
初步公告
如果可能,请勿使用预先通知。的#include需要的头文件来代替。
定义
“预声明”是没有相应定义的类,函数,模板的声明。
每
- . #include ( ) .
- . #include - .
- , .
- API, . , 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*)
#include B D, test() f(void*). - , .
- 允许进行初步声明(以及进一步使用指针作为类成员)的代码结构会使代码造成混乱和缓慢。
判决
- 尽量避免预先声明在另一个项目中声明的实体。
- 使用头文件中声明的函数时,请始终#include该文件。
- 使用类模板时,最好#include其头文件。
另请参阅“名称”和“包含顺序”中的包含规则。
内联函数
仅当函数较小(例如,不超过10行)时,才将函数定义为内联函数。
定义
除了标准的函数调用方式外,您还可以将函数声明为内联函数,并告诉编译器将其直接包含在调用代码中。
优点
使用内联函数可以生成更有效的代码,尤其是当函数较小时。将此功能用于获取/设置功能,其他简短和对性能至关重要的功能。
反对
内联函数的过度使用会使程序变慢。同样,内联函数根据其大小可以增加或减小代码的大小。如果这些功能很小,则可以缩小代码。如果函数很大,则代码大小会非常大。请注意,在现代处理器上,由于更好地使用了指令高速缓存,精简代码的运行速度更快。
判决
一个好的经验法则是不要把功能inlineable如果他们超过10行代码。避免使析构函数成为可内联的,因为它们可以隐式包含许多额外的代码:调用变量析构函数和基类!
另一个好的经验法则是,对于具有循环或switch语句的内联函数通常没有任何意义(除非在退化的情况下永远不会执行循环或其他语句)。
重要的是要了解,内联函数不一定会以这种方式编译为代码。例如,通常使用标准调用来编译虚函数和递归函数。通常,不应将递归函数声明为内联函数。进行内联虚拟函数的主要原因是将定义(代码)放在类定义本身中(以记录行为或可读性)-通常用于get / set函数。
名称和包含顺序
按照以下顺序插入头文件:配对文件(例如foo.h-foo.cc),C系统文件,C ++标准库,其他库以及您的项目文件。
所有项目标头必须相对于项目源目录,而不能使用UNIX别名(例如)。(当前目录)或..(父目录)。例如,应该像这样包含google-awesome-project / src / base / logging.h:
#include "base/logging.h"
另一个示例:如果dir / foo.cc和dir / foo_test.cc文件的主要功能是实现并测试dir2 / foo2.h中声明的代码,则按以下顺序编写头文件:
- DIR2 / foo2.h。
- —
- C (: .h), <unistd.h>, <stdlib.h>.
- —
- C++ ( ), <algorithm>, <cstddef>.
- —
- .h .
- .h .
用空行分隔每个(非空)文件组。
当配对的头文件(dir2 / foo2.h)中缺少所需的头文件(系统等),并且对应的dir / foo.cc或dir / foo_test.cc文件的组装失败时,此文件顺序使您能够检测到错误。结果,该错误将立即显示给使用这些文件的开发人员(而不是仅使用外部库的其他团队)。
通常,配对的文件dir / foo.cc和dir2 / foo2.h位于同一目录中(例如,base / basictypes_test.cc和base / basictypes.h),尽管这不是必需的。
请注意,诸如stddef.h之类的C头文件通常可以与相应的C ++文件(cstddef)互换。可以使用任何变体,但是最好遵循现有代码的样式。
在每个部分中,头文件最好按字母顺序列出。请注意,先前编写的代码可能不遵循此规则。如果可能(例如,更正文件时),请将文件的顺序更正为正确的顺序。
所有包含声明所需类型的头文件都应包括在内,但预先声明时除外。如果您的代码使用bar.h中的类型,请不要依赖另一个文件foo.h来包含bar.h并且您可以将自己限制为仅包含foo.h:显式包含bar.h(除非(在文档中可能已明确声明)foo.h也将为bar.h提供类型)
例如,google-awesome-project /src/foo/internal/fooserver.cc中的头文件列表可能如下所示:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
例外
在某些情况下,您需要根据预处理器的条件(例如,取决于所使用的OS)包括头文件。尝试使包含尽可能短(本地化),并将其放置在其他头文件之后。例如:
#include "foo/public/fooserver.h"
#include "base/port.h" // For LANG_CXX11.
#ifdef LANG_CXX11
#include <initializer_list>
#endif // LANG_CXX11
注意:
图片取自开源。