加入的机器人智能实验室这周布置的题目是翻译理解一篇google C++ Style Guide 的文档的Header File部分
原文档链接:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
我借助google 翻译勉强不通顺的把Header File部分翻译出来了
而且我本身刚开始学C++,所以对文档的大部分不是很懂
翻译如下:
在一般情况下,每个cc后缀文件都应该有一个与之相关联的h后缀文件,当然,也有一些例外,比如说小的项目只包含一个main()函数
正确使用头文件可以使你的代码的可读性,性能产生巨大的不同
下面的规则将引导你如何正确使用头文件。
(1)#define预处理
所有的头文件都应该有#define预处理以防止被多次包含,对象名的格式应该是<PROJECT>_<PATH>_<FILE>_H_
为了保证唯一性,就应该根据一个项目的源代码中的完整路径,例如在foo工程中的文件foo/src/bar/baz.h 就应该有如下的预处理:
#ifndefFOO_BAR_BAZ_H_
#defineFOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
(2)头文件依赖
当前置声明就够用了的时候不要使用#include。
当你包含了一个头文件,你就产生了一种依赖,这种依赖会导致任何头文件的改变都会使代码需要重新编译,如果你的头文件里再包含了别的头文件,任何对这些文件的改变都会导致包含头文件在内的代码重新编译,所以,我们更愿意减少头文件的包含,特别是当头文件里包含了别的头文件。
你可以显著的降低头文件的书目,你需要在你的头文件里使用前置声明,比如说,如果你的头文件使用了File类,并且不要求访问File的声明,那么你可以在你的头文件里前置声明File类,而不是#include”fire/base/file.h”
我们如何才能不去访问它的定义而使用一个再头文件里的Foo类?
* 我们可以声明FOO*或者Foo&的数据类型
* 我们可以声明(但没有定义)一个使用Foo类型的参数或者返回值的函数(一个例外是如果一个Foo或者 const Foo&类型的参数有一个不明确的单参数构造函数,在这种情况下,我们需要完整的支持自动类型转黄的定义)
* 我们可以声明静态数据成员的Foo类型。这是因为静态数据成员定义在类定义外部。
另一方面,如果你的类存在Foo子类或者有Foo类的数据成员,你就必须包含一个头文件
有时候,这样会对指针(或更好的情况,scoped_ptr)成员而不是对象成员有很大的意义,但是,这样会使代码的可读性降低和损失一部分性能,所以,如果唯一的目的是想尽量减少对头文件的包含,那就避免这样做。
当然 .cc文件通常需要他们所使用的类的定义,并且通常包含数个头文件。
笔记:如果你再你的源文件里使用Foo符号,你应该自己定义Foo类,无论是通过#include还是前置声明,不要指望Foo通过一个并没有直接包含的头文件传递,一个例外是如果Foo在myfile.cc被使用,那么在myfile.h而不是myfile.cc上#include或者前置声明是可以的。
(3)内联函数
定义在内部的函数,当他们非常小,或者说,比10行还短
定义:你可以声明一个要求编译器扩大内联的函数而不是要求他们通过正常的函数调用机制
优点:内联一个函数可以生成更有效的目标代码,只有内联函数足够小
缺点:过度使用内联会使程序速度较慢。根据函数的大小,内联可能会导致代码增加或减少。内联一个非常小的存取函数通常会减少代码,而内联一个非常大的函数会极大地增加代码。在现代的处理器,更小的代码通常运行更快,因为可以更好的利用指令高速缓存。
决定:一个说的过去的经验法则是不要内联一个超过10行的函数,当心destructors,因为它由于隐式成员和基础destructor的调用,会比他们表现的更长。
另一个有用的经验法则是。内联一个循环或者switch语句都不符合效益(除非在正常情况下,循环或者switch语句永远不会执行)
重要的是要知道函数并非总是内联即使它们被声明为这样,例如,虚拟和递归函数通常不内联。通常递归函数不应该是内联。使虚拟函数成为内联函数的主要原因,是将其定义在类中,为方便起见,或记录其行为,例如,对accessor和mutator。
(4)-inl.h文件
你可以在需要的时候使用-inl.h来定义复杂的内联函数
内联函数的定义必须是在头文件中,这样编译器可以调用内联函数的定义,然而,实现代码部分一般在.cc文件,我们不喜欢有太多的实现代码在.h.文件,除非有可读性或性能上的优势
如果一个内联函数的定义很多,在很少情况下,你应该把你的代码放在头文件里,例如:accessors和mutators当然应该在一个类的定义里面,更复杂的内联函数为了实现者和调用者的方便也可以放在头文件中,即使这使得头文件太过笨重,你还是可以不把代码放在.inl.h文件中。这分离了类的定义和实现,当然在必要时也允许实现包含在内。
另一种使用-inl.h文件的必要时为零函数模板的定义,这可以使得你的模板定义便于阅读
也别忘了一个-inl.h文件像别的头文件一样需要一个#define命令
(5)功能参数顺序
当定义一个函数时,参数的顺序是:输入,然后才是输出
C/C++的函数参数都是输入进函数,从函数输出,或者两者都有,输入参数通常是值或者const引用,而输出和输出/输入参数将是没有const的指针,当调用函数参数时,将所有的输入参数在输出参数之前,特别的,不要仅仅因为它们时新的就添加新参数到函数的底部,只在输出参数前放置新的输入参数
(6)include的名称和顺序
为了使用标准的可读性,请避免隐藏的依赖关系:C库,C++库,其它库,你的项目.h
为了不使用UNIX目录的快捷方式,所有项目的头文件应该被列为项目的源代码的后裔,(当前目录)或(父目录),例如,
google-awesome-project/src/base/logging.h应包含在
#include"base/logging.h"
在dir/foo.cc ordir/foo_test.cc,它主要的目的时实现或测试dir2/foo2.h里面的东西,这就要求你include如下:
- dir2/foo2.h (preferred location — see details below).
- C system files.
- C++ system files.
- Other libraries' .h files.
5.Yourproject's .h files.
对于优先顺序,如果dir/foo2.h忽略了任何必要的包括,生成的目录/ foo.cc或dir/ foo_test.cc将会break。因此,这个规则确保生成break显示的是第一个对这些文件工作的人,而不是其他包中的无辜的人。
Dir/foo.cc和dir/foo2.h通常在同一个目录下(比如base/basectypes_test.cc和base/basectypes.h)但也可以在不同目录下
在每一个部分,按照字母排列顺序是不错的组织方案
比如说,在google-awesome-project/src/foo/internal/fooserver.cc中的include可能会是这样的额:
#include"foo/public/fooserver.h" //Preferred location.
#include<sys/types.h>
#include<unistd.h>
#include<hash_map>
#include<vector>
#include"base/basictypes.h"
#include"base/commandlineflags.h"
#include"foo/public/bar.h"