头文件包含问题:不可混乱相互包含!
[編輯] [转简体] (简体译文)概要
正文
今天研究了很一会,终于稍微明白一点头文件的包含问题了……
背景
问题背景是:两个头文件对应两个 cpp,两个头文件声明的内容相互用到。
假设这两个头文件叫 A.h 和 B.h 吧!他们分别声明了类 A 和 B。并且两个类中分别用到对方。
这样的话,我当时就马上使用了最错误的方案:新建第三个头文件(比如叫 god.h),然后在 god.h 里面 #include 这两个头文件,最坏的是,我还在 A.h 和 B.h 中都去 #include "god.h"。最后,我在 main.cpp 中包含 god.h,从而十分“聪明地”构成了完美闭环,各个模块相互包含,似乎天衣无缝。
然而,这种做法大错特错……我们先假设每个头文件都设置了 #pragma once,按这样的包含关系,由于最后展开是线性的,所以一定有其中一个类会报错(比如 A 报错,就会显示 B 类未定义)。
我发现这样不行之后,就兴致勃勃地转向另一种方案:在 A.h 中包含 B.h,在 B.h 中包含 A.h,而 main.cpp 还是包含 god.h 不变。
这样又犯了相同的错误,AB 两个文件相互包含,最后肯定有一个展开在前面,有一个在后面,那么在前面的那个类就肯定会认为后面那个类未定义。
解决办法
假设 AB 两个类都只是用到对方的指针,那么就最好办。这个时候不用再在 AB 的头文件里面包含对方了,应该在 A.h 中声明 class B,在 B.h 中声明 class A,再在各自的 cpp 实现文件中包含对方的头文件。
如果说类 A,B 属于不同的命名空间,就应该在声明的时候这样写:
namespace _A { class A; }
类 B 的声明也一样。
注意这种解决办法是针对两个类分别引用对方的指针的。如果说直接用了对方的对象,那么还会编译错误:使用未定义的类 A。
而且,这种情况下,头文件里面不能写实现!(给我长了个教训,我以后尽量不在头文件里面写实现了)
因为现在实现是写在 cpp 里面的,cpp 包含了对方的头文件,所以写实现没有问题。但是头文件里面还只是声明了一下而已,要是写了关于另一个类的实现,也会报错:使用未定义的类 A。
另一种情况,如果在 A 类中用了 B 类的对象而不是指针,就必须要在 A.h 中包含 B.h 了。但是 B.h 不可再去包含 A.h 了,否则又会陷入相互包含的泥潭。此时建议 B 就用 A 的指针吧,这样只要在头文件里面声明 A 就行了。
总结:
头文件不能相互包含
如果 A 类包含了 B 类的指针,那么只需要在 A.h 里声明 B 类,在实现中包含 B.h 即可(不要在头文件里面写实现!)
如果 A 类包含了 B 类的对象,那么 A.h 就必须包含 B.h
头文件的包含尽量减少,如果要包含,可在实现文件里面包含,头文件里面写声明。否则包含多了的话,头文件之间就容易形成错综复杂的包含关系。
不足:
这回是引用对方的类,如果是调用函数,那就要把声明都抄一遍,十分麻烦。所以还是多用类吧。
参考:
https://blog.csdn.net/changbaolong/article/details/6916441
https://blog.csdn.net/wangbingqian_110/article/details/79473862
https://blog.csdn.net/a812073479/article/details/38542515