C++头文件错误包含所导致的相关错误以及解决办法
这里总结下目前遇到的C++头文件错误包含所导致的相关错误以及解决办法。错误有两种:
- 循环依赖
- 重复编译
循环依赖
b.h文件内容
#include “a.h”
class B{};
a.h文件内容
#include “b.h”
class A{};
main.cpp文件内容
#include "a.h"
#include "b.h"
int main()
{
};
运行后,得到报错语句:
In included file: main file cannot be included recursively when building a preamble
即在包含的文件中:在构建序言时不能递归地包含主文件。
这句话怎么理解呢?我们知道程序预编译过程中,编译器会把头文件包含指令换成对应文件的代码。这里的“代码”就包括自身文件中已经替换头文件包含指令的代码。
两个互相包含对方头文件的头文件进行预编译的时候,就会进行递归式的代码替换操作。就像两面镜子,正对着对方,两面镜子不对映射着对方,无穷无尽,没有尽头。这样代码文件就会越来越大,直到内存装不下,编译器肯定是不会允许这种情况出现。
我们已经知道头文件相互包含的危害了。现在得分析下,什么情况下我们会在两个类各自的头文件不自觉地包含对方的头文件。会有两种情况:
- A的一个成员变量的类型是B,且B的一个成员方法中要用到A类对象
- A的一个成员变量的类型是B,且B的一个成员变量的类型是A
第一种情况其实很好解决,我们只需要在B的cpp文件中,包含A的头文件即可,而不是在B的头文件中包含A的头文件。
第二种情况,需要在A的头文件中添加class B;语句
class B;
class A
{
A();
private:
B* b;
};
然后,在A的cpp文件中包含B的头文件
#include "b.h"
A::A()
{
b = new B();
}
重复编译
如下案例,由于A.h不再包含b.h,也就没有循环依赖的问题。但是会存在重复编译的问题。
重复编译出问题的本质原因是一个编译单元中的任何变量,类只能定义一次。
补充下:一个cpp文件进行预编译后,即成为一个编译单元。
b.h文件内容
#include “a.h”
class B{};
a.h文件内容
class A{};
main.cpp文件内容
#include "a.h"
#include "b.h"
int main()
{
};
上述代码中,main.cpp文件就是一个编译单元,由于b.h包含了a.h,这就导致main.cpp进行预编译后的编译单元中出现了两个A类。如果不做防止重复编译处理,即会导致编译器对A进行编译两次,即定义两次。
解决办法是:在重复包含的头文件最开头使用预编译指令
#pragma once
这个指令就是告诉编译器,第二次检测到该头文件时,不用再次编译,直接用第一次编译好的文件即可