编码规范之头文件

 

通常,一个.cc文件都会有一个关联的.h文件。有一些例外情况,比如一个单位和小的.cc文件仅包含一个main()函数。正确的使用头文件能够让可读性,代码大小和代码表现都有很大不同。

 

下面的规则将指导你通过在使用头文件中容易犯得各种错误。

 

一.#define控制

所有头文件都要有#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

当你包含了一个头文件你就引进了一个附属,就是说一旦你包含的头文件改变了,就会引起你的代码被再编译。如果你的头文件包含了其他头文件,任何对于被包含的头文件的改变都将会引起包含你的头文件的任何代码的再编译。因此,我们倾向于最小化包含,尤其对于一个头文件包含其他头文件。

 

你可以用前向声明来最小化你自己的头文件中要包含的其他头文件数目。例如,如果你的头文件使用到了File类,并且不需要使用File类的声明,你的头文件只需要前向声明class File;而不必是#include "file/base/file.h"

 

我们怎么才能在头文件中使用一个类Foo并且不涉及到他的定义呢?

 

l         我们可以声明一个成员变量,变量类型是Foo* 或者 Foo&.

l         我们可以声明(不是定义)函数,这个函数带有Foo类型参的数或者返回值(一个例外就是一个参数Foo 或者 const Foo&有一个不明确的单参数构造函数,这种情况我们需要包含Foo类型的全部定义去支持自动类型转换)

l         我们可以声明Foo类型的静态成员变量。这是因为静态成员变量的定义是独立于类定义之外的。

另外一方面,如果你的类继承了Foo类或者拥有一个Foo类型的成员变量,你就必须为Foo包含头文件。

 

有时拥有一个指针成员变量要比拥有一个对象成员变量来的明智。但是这样会降低可读性,并且强加了一种不利后果的表现,所以应避免仅仅是为了最小化头文件包含而进行的这种转变。

当然,.cc文件需要它使用的类的定义,所以通常包含一系列的头文件。

 

备注:如果你在你的源文件中使用了符号Foo,你必须通过#include或者前向声明自己引入Foo的定义。不要依赖于间接包含的头文件中引入的符号Foo。一个例外情况是,如果Foo被用于myfile.cc,则可以在myfile.h#include或者前向声明Foo,而不必是myfile.cc

 

三.内联函数

只有当一个函数很小,小于10行的情况下,才将它定义为内联函数。

l         定义

你可以声明一些函数,允许编译器内敛地展开它们,而不是通过通常的函数调用机制来调用它们。

l         骗局

过度使用内联实际上会使程序变慢。内联能够使代码大小增长或者减少,这随函数大小而定。内联一个非常小的存取函数一般会减少代码大小,但是内联一个非常大的函数则会增加代码大小。

在现在的处理器上,由于更好的利用了指令告诉缓冲存储器(instruction cache),小的代码往往运行的较快。

l         结果

通过经验得出的一个合乎要求的规则是不要内联一个大于10行的函数。注意一下析构函数,它们通常比他们实际表现出的要长,因为内含的成员和基类析构函数的调用。

通过经验得出的另一个合乎要求的规则是:不要花费效率去内联一个包含循环或者分支的函数(除非在一般情况下,循环或者分支从不被执行到)。

 

重要的一点是:即使有些函数是被声明为内联的,但是他们并非总是内联;例如,虚函数和递归函数通常是不内联的。通常递归函数必须不内联。让一个虚函数内联的主要原因是要把它的定义放在在类里面,为了方便或者为了记录他的行为,比如存取函数,设置函数。

l         -inl.h文件

当需要的时候你可以使用带-inl.h后缀的文件名去定义一个复杂的内联函数集。

一个内联函数的定义需要放在一个头文件里,以便于编译器在调用内联函数的地方能找到内联函数的定义。但是,执行代码其实是属于.cc文件的,并且我们不喜欢拥有太多的实际代码在.h文件中除非有很好的可读性或者有性能优点。

 

如果一个内联函数定义很短,拥有很少的逻辑,你应该把代码放在你的.h文件中。例如,访问器和设置器无疑要房子类定义里。更为复杂的内联函数也可以放在.h文件中一位了执行和调用的方便,但是如果这让.h文件太过难于驾驭控制,你可以在一个分离的-inl.h文件里代替这部分内联函数。这把执行从类定义中分离开来了,同时也允许执行被需要的时候包含。

 

-inl.h的另外一个作用是让函数模板更加轮廓鲜明。可以让你的模板定义容易阅读。

不要忘记-inl.h文件需要像其他头文件一样的#define控制。

四.函数参数顺序

当定义一个函数时,参数顺序是:输入参数,然后是输出参数。

C++的函数参数或者是输入,或者是输出,或者两者都是。输入参数通常使用值传递或者const引用传递,输出参数和输入/输出参数使用的是非const的指针类型。当排序函数参数的时候,让所有输入参数排在任何输出参数之前。特别的,不要仅仅因为是一个新参数就把一个新的参数加在最后;把新的输入参数放在输出参数之前。

 

这并不是一个困难且快速的规则。那些既是输入又是输出的参数(通常是类或者结构体)通常会搅乱规则,所以通常,相关函数的一致性或许要求你改变规则。

五.包含的名称和顺序

为了可读性和避免隐藏的重复包含,使用标准的包含顺序:c库,c++库,其他库,系统.h,用户.h

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值