1、不要include没有用的头文件(Unnecessary Compiling)
“不要include没有用的头文件”,这句话看似简单,也很奇怪,但实际项目过程中,随着产品的不断演进,代码越来越庞大,维护人员几经移手,往往会有冗余的头文件被包含。
多余的头文件包含不仅造成模块间的耦合,在编译器进行预处理时,还要对头文件进行展开,耗时耗力。在大的项目中,全编译一次几个小时的情况下,这个坏习惯只会给系统带来更大的负担。
2、尽量不要在头文件中include头文件
看过很多人写代码的时候,喜欢在头文件中将所有用到的头文件include,这样写的代码看起来很美,但绝对不可取。
举个例子:在A.h中include了B.h,而C模块在使用时需要用到A.h,但又不需要用到B.h中的内容,这时候怎么办?不好意思,没办法,强买强卖,你要用A,必须把B也用起来,这样无疑增加了C模块与B的耦合(当然,也会增加编译预处理开销)。
另外,在头文件中include头文件,会导致一种场景:“循环include”。举个例子:
1)在A.h中,include B.h;
2)在B.h中,include C.h;
3) 在C.h中, include A.h;
这样做的直接后果就是导致编译器“崩溃”。
熟悉编译链接过程的应该清楚,预处理阶段会对所包含的头文件进行展开。ok,现在编译器开始处理A.h,发现其包含了B.h,就将B.h的展开,发现其包含了C.h,于是又将C.h展开,发现其包含了A.h,然后又去展开A.h,然后发现其包含了B.h,就将B.h展开……子子孙孙无穷尽也。
可能,这么简单的例子看着比较简单,出现问题也比较好定位,但在几万k的代码中出现这样的问题,编译器报的错往往是千奇百怪,让人哭笑不得,那个时候,怎么去定位?
其实,头文件可以理解成模块的“说明书”,在头文件中只需要知道我需要使用哪些类,需要有哪些方法即可,除非确实需要使用头文件中的内容,如常量定义、宏定义,否则不要轻易的在头文件中include头文件。
正确的做法:在头文件中声明需要使用的类/函数,在实现的位置包含相关的头文件。示例:
文件 test.h:
#include <iostream>
using namespace std;
class Test
{
void dump()
{
cout << "Hello world" << endl;
}
};
文件 main.h:
#include <iostream>
using namespace std;
class Test ; // 注意:这里只用声明
class MainApp{
public:
void sayHello();
private:
Test t;
}
文件 main.cpp:
#include "main.h"
#include "test.h" // 这里进行include
void MainApp::sayHello()
{
t.dump(); // 这里才使用到了Test类的内容
}