谷歌C++编程规范(一)----头文件

        今天晚上在网上发现了谷歌的C++编程规范这个东西,大家貌似都非常推崇的样子。然后又在网上搜了一下,好像并没有中文版的。所以,我就打算把它们全都翻译一遍啦,既能提高自己的英语水平,还有助于养成良好的编程风格,何乐而不为呢?哈哈~

        整个编程规范包括九个部分,所以就分成九篇文章来写啦。本篇文章主要是背景介绍以及C++头文件的编程规范,下面直接进入正题。

背景:

       C++是谷歌很多开源项目的主要开发语言。每一个C++程序猿都知道,C++有许多非常强大的特性,但是同时,这些强大的特性又会增加代码的复杂性,让代码更易错,并且变得难以阅读和维护。所以,写这篇文章的目的就是要通过描述在用C++编码时那些该做的和不该做的事情来控制语言的复杂性。下面的这些规则在允许程序猿高效地使用C++语言特性的同时保证了代码的基本可控性。

       编码风格,通常叫做可读性,是我们对于管理代码时所用规范 的总称。其实“编码风格”这个词是不太恰当的,因为这些编码规范并不仅仅指源文件的规整。我们保持代码基本可控性的方法之一就是要求实行前后一致性。这是非常重要的,因为只要这样才能保证一个程序猿能阅读另一个程序猿的代码,并且快速理解它。保持统一的编码风格并且遵循相应的规范意味着我们能够用“模式匹配”去推断变量标示符。运用逗号,要求的惯用语法和模式能够让代码变得更加易懂。当然,在有些情况下,改变确定的编码风格也许会取得更好的效果,但是我们选择不这样做,因为我们要保持编码的前后一致性。

       本篇文章想要表达的另一件事情是C++语言特性的膨胀。C++是一门拥有许多高级特性的非常庞大的语言。有时候,我们限制甚至禁止某些特性的使用,借此保持代码的简洁并且避免这些特性将会导致的各种各样的错误和问题。这篇指导列举了这些特性并且说明了为什么要限制对于它们的使用。

      谷歌的开源项目都要求遵从这篇指导。并且需要注意的是,这篇指导并不是C++的教程:我们假设读者已经对C++非常熟悉了。

头文件:

        一般来说,每个.cc文件都有与之相关联的.h文件。当然,也存在不少的例外,比如单元测试或者那些仅包含一个main函数的小的.cc文件。对于头文件的正确使用能对你的代码的可读性,性能,体量都带来极大的改变。接下来就的这些规则就会列出使用头文件时容易犯的各种各样的错误。


#define 防护

       每个头文件都要定义#define以防止多重包含,并且采用<PROJECT>_<PAH>_<FILE>_H这样的形式。为了保证唯一性,它们应当基于项目的源文件树的绝对路径进行命名。比如项目foo中的文件foo/src/bar/baz.h就应当使用如下的定义形式:

#ifndefine FOO_BAR_BAZ_H
#define     FOO_BAR_BAZ_H
.....
#endif      //FOO_BAR_BAZ_H

头文件依赖性

       当你用前置声明能够满足需求时,不要使用#include。当你包含头文件的时候,你就引进了文件的依赖性,也就是说,当你包含的头文件改变时,当前文件的代码要全部重新编译。并且,当你的头文件包含了其他头文件的时候,这些头文件的改变,都会导致包含你的头文件的代码重新编译。因此,我们要最小化减少头文件的包含,特别是在头文件中包含其他头文件。

       最好的在头文件中少包含的头文件的方法就是使用前置声明。比如,当你的头文件中要使用类File,但又无需知道类File的具体声明。那么你可以直接前置声明class File而不是#include "file/base/file.h"。那么我们如何使用类File而无需通过它的定义呢?

1.声明数据类型成员为Foo*或Foo&

2.我们可以声明(而不是定义)参数或者返回值类型为Foo的函数。(除了参数Foo或者const Foo&有一个non-explicit的单参构造函数,这种情况下我们需要完整的定义以支持自动类型转换)

3.我们可以声明数据类型为Foo的静态变量。因为静态数据成员在类的定义之外定义。

另外,你必须包含Foo头文件,如果你的类以Foo为子类或者包含有类型为Foo类的数据成员。

       有时候,使用指针成员(最好是scoped_ptr)而不是对象成员是更好的选择。但是,这对代码的可读性和性能会造成损害。所以,如果仅仅是为了减少头文件的包含,最好尽量避免这种形式的转换。当然,一般情况下,.cc文件是需要知道它使用类的具体定义的并且需要包含相应的头文件。

注:当你在源文件中使用Foo标示符时,要通过#include或者前置声明来引入。而且不要依赖那些通过间接头文件包含获得的标示符。除了,如果在myfile.h中#include(或者前置声明了)Foo,那么在myfile.cc中仍可使用。


内联函数:

       当函数很小,一般来说10行以内,可以将其定义为内联。

定义:当声明函数为内联时,编译器会将其扩展为内联而不是用通常的函数调用机制去使用它们。

优点:如果内联函数足够小的话,那么将它内联化将大大提高目标代码的效率。对于存取器,修改器或者其他短小的,对性能要求较高的代码,可自由使用内联。

缺点:事实上,如果过度使用内联,反而会让程序变慢。根据函数的大小,内联可以让代码量增加或者减少。当内联一个非常小的存取器函数时,代码量通常会减少。反之,内联一个较大的函数时反而会增加代码量。在现代处理器上,简短的代码通常运行得更快,因为它们能更好地利用高速缓存。

选择:比较合适的选择是,当代码行数超过10行时就不选择内联了。我们可以想到析构函数,它们通常比看起来要长,因为它们需要调用成员和基类的构造函数。另一点需要注意的是:如果内联函数中有循环或switch语句时并不是非常高效。(除非,通常情况下,循环或switch语句并不执行)而且,即使被声明为内联,该函数也不一定作为内联函数执行。比如,虚函数或者递归函数不被默认内联。通常,递归函数不应该被内联。将虚函数内联的主要目的是将它定义在类的内部,为了方便或者记录它们的行为。


.inl.h文件:

         当你要定义复杂的内联函数的时候,你可能会用到后缀为-inl,h的文件。一般内联函数的声明都放在头文件中,这样在函数调用的时候就编译器就会将其作为内联函数。但是函数的实现代码一般都放在.cc文件中。事实上,我们不希望有太多的实现代码放在.h文件中,除非这能带来更好的可读性或性能。如果内联函数的定义非常短小,并且包含很少的逻辑,那么你应该将它放在.h文件中。比如,存取器和修改器的代码就应当放在类的定义中。当然,如果为了实现和调用的方便,更复杂的内联函数也能放在.h文件中,尽管这会让.h文件显得非常笨拙。因此,将它们放在-inl.h中可能是更好的选择。这能够将内联函数的实现和类的定义分开,并且仍能够在需要的时候将实现包含进去。

        -inl.h文件的另一个作用是模板的定义。它能让模板的定义更加易读。别忘了,和其他头文件一样,-inl.h同样需要使用#define防止重定义。


函数参数顺序:

       当定义一个函数的时候,参数的顺序应当是先输入后输出。

      在C/C++中,函数的参数无非是输入,输出或者两者皆有。输入参数一般都是值或者const类型的引用。而输出或者输入/输出参数通常都为non-const的指针。我们在放置函数参数的时候,应当将只用于输入的参数放在输出参数前面。特别地,不要将新增加的参数放在参数的最后。如果新增加的参数是只用于输入的,那么将它放在输出参数的前面。

      这并不是必须遵守的规则,那些既是输入又是输入的参数(通常为class/struct)会将这一切全都搞乱,并且有时候为了和相关函数保持一致性,你也需要违背这条规则。


命名和包含的顺序:

     使用标准的包含顺序不仅可以是为了可读性,更加可以避免隐藏的库之间的依赖性,包括C库,C++库,其他库,以及你自己的项目的头文件。项目的所有头文件都应当按照项目源目录的降序排列,并且不能使用UNIX的目录快捷方式.(当前目录)和..(父目录)。比如,google-awesome-project/src/base/logging.h应该按如下方式被包含:

#include "base/logging.h"
dir/foo.cc主要用于实现或是测试dir/foo2.h中的事务,那么你应当按如下顺序包含:

1.dir2/foo2.h

2.C系统文件

3.C++系统文件

4.其他库的头文件

5.你自己项目的头文件

这样的优先级顺序降低了隐藏的依赖性。我们希望每个头文件都能自己编译。而实现它最好的方式就是每个头文件都是某些.cc文件中第一个被#include的.h文件。通常dir/foo.cc和dir2/foo2.h会在同一个目录下(又如,base/basictypes_test.cc和basetypes.h),但是它们也可以不在一个目录下。在每一类库的内部,头文件又应当按照字典序排列。比如,google-awesome-project/src/foo/internal/fooserver.cc就应当按如下方式包含:

#include "foo/public/fooserver.h"

#include <sys/types.h>
#include <unistd.h>

#include <hash_map>
#include <vectro>

#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/public/bar.h"

注:某些地方翻译地不是很好,原文中有些地方也比较啰嗦晦涩。

原文地址:http://www.open-open.com/doc/view/1b6aae74429143f995fdbcf36aafbb48





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值