一般来说,每一个
.c
或者.cpp
文件对应一个头文件(.h
文件),当然,也有例外,例如一些测试单元或者main
文件,头文件的一些规范可以令代码可读性、程序的性能等大为改观,所以还是要注意头文件的规范问题。
一、#define保护
所有头文件为了防止文件被多重包含(multiple inclusion
),一般就需要#define
保护。#define
保护的格式如下:
<PROJECT>_<PATH>_<FILE>_H_
例如:
#ifndef FOO_BAR_BARZ_H_
#define FOO_BAR_BARZ_H_
...
#endif //FOO_BAR_BARZ_H_
这个是比较老的一个做法了,所以很多编译器基本都是可以兼容的,但是还有另一个语句也是可以起到相同作用,但是一些太老的编译器据说用不了的(但是我好像没遇到过),VS下好像默认新建一个类的头文件就会用这个新的:
#program once
这一种相对于第一种的好处是,不通过#define
一个变量来起到防止重复包含,所以,不存在重复变量的问题,#define
后面的变量在同一个工程里是不能重复的,重复的话是只认一个的,就可能导致另一个文件编译的时候被排除。
二、头文件依赖
在头文件中减少包含其他头文件,改用前置声明(forward declarations
),理由是:头文件被包含的同时会引入一项新的依赖(dependency
),只要头文件被修改,代码就要重新编译,如果你的头文件包含了其它头文件,这些头文件的任何改动都将导致那些包含了你的头文件的代码重新编译。
使用前置声明就是在头文件中添加:
class Foo;
然后在对应的源文件中包含对应的头文件
#include <Foo.h>
可以这么做的几种情况:
1)、将数据声明为指针(Foo*)或者引用(Foo&);
2)、参数、返回值为Foo的函数只声明不定义;
3)、静态数据成员可以被声明为Foo,因为静态数据成员的定义在类定义之外;
但是,如果你的类是Foo的子类
或者包含类型为Foo
的非静态成员数据,则必须包含头文件。
补充说明,什么是声明,什么是定义,一般在头文件中经常看到一个函数,有返回值有输入参数,但是没有主体,或者一个变量声明为指针,但是没有new
等操作,像这样的就是声明:
Foo *pFoo;
void setInputNumber(int num);
然后是定义,定义的话就是有函数主体,或者说就有变量的内存操作,包括非指针变量的声明也已经包含了定义,像这样的:
int nInputNumber;
int nCount = 0;
pFoo= new Foo();
void setInputNumber(int num)
{
nInputNumber = num;
}
三、内联函数
当函数小于10行的时候可以定义为内联函数(inline function
)。
定义:当函数被声明为内联函数后,编译器会将其内联展开,无需像通常的函数调用机制一样来调用内联函数。
优点 | 缺点 |
---|---|
可以令目标代码更高效。适合存取函数或一些较短的关键代码 | 滥用内联适得其反,内敛较大代码(如果编译器允许),将增加代码量 |
不可用或者尽量不要用内联函数的情况:
1)、超过10行代码;
2)、析构函数;
3)、递归函数(可能大多数编译器不支持);
4)、包含循环或者
switch
语句的函数;
四、头文件包含实现代码
一般来说,头文件是只包含声明,而不要包含实现代码的,但是用到内联函数或者函数模板的话就需要将代码实现写在一起,为了可读性,是可以新建一个后缀为-inl.h的头文件,专门用于存放内联函数和模板函数,这样可以增强代码可读性。
需要注意,这样的头文件也要加#define
保护的。
五、函数参数顺序(Function Parameter Ordering)
顺序:输入参数在前,输入输出参数居中,输出参数在后。
或者:输入参数在前,输入输出参数居中,输出参数在后,控制参数垫后。
输入参数一般为传值或者常数引用(const references
),输出或者输入输出参数一般为传非常数指针(non-const pointers
),有些参数是输入参数,但是属于控制变量意义的参数,那我是觉得应该放最后面的。
六、包含头文件顺序
如果我有一个命名为Foo.h的头文件以及一个对应的Foo.cpp的源文件,那么,头文件中的包含顺序应该是:
C库 > C++库 > 其他库的.h文件 > 项目内的.h文件;
而源文件中的包含顺序应该是:
#include "Foo.h" > C库 > C++库 > 其他库的.h文件 > 项目内的.h文件
还有一点强迫症的可以将同目录下的头文件按首字母顺序来排列。
补充说明,#include ""
和#include <>
的区别:
1)、#include <>
引用的是编译器的类库路径里面的头文件;一般编译器会在编译器设置的include目录和系统中的INCLUDE环境变量中找头文件;一般用于标准文件
;
2)、#include ""
引用程序目录的相对位置的头文件;一般是先从当前文件所在的文件夹内找,找不到再去编译器设置的include目录或者系统的INCLUDE环境变量中找;一般用于自定义的文件
。