注:类的交叉引用本身就是一种不好的设计,但是,有时我们又不得不这样用。
<一>问题
两种常见情况:
(1)直接相互引用
模型:
A.h中#include "B.h"
B.h中#include "A.h"
(2)传递式相互引用(或者叫环形)
模型:
A.h中#include "B.h"
B.h中#include "C.h"
C.h中#include "A.h"
B代表中间层,可以是多个
<二>现象
以下面的代码为例:
main.cpp
- #include <iostream>
- #include "class_a.h"
- #include "class_b.h"
- int main()
- {
- A aa;
- aa.InvokeB();
- B bb;
- bb.InvokeA();
- }
- #ifndef CLASS_A_H
- #define CLASS_A_H
- #include "class_b.h"
- class A
- {
- public:
- void InvokeB()
- {
- B *b;
- b->Print();
- }
- void Print()
- {
- std::cout << "This is class A\n";
- }
- };
- #endif
- #ifndef CLASS_B_H
- #define CLASS_B_H
- #include "class_a.h"
- class B
- {
- public:
- void InvokeA()
- {
- A *a;
- a->Print();
- }
- void Print()
- {
- std::cout << "This is class B\n";
- }
- };
- #endif
如果去掉这个保护机制,又会引起“重定义”或者“包含文件太多,深度=1024”等问题。
<三>解决办法
(1)提前声明
这个虽然可行,但是存在风险。这种方法切记不可使用类名来定义变量和函数的变量参数,只可用来定义引用或者指针。因为前导声明只是一个符号声明,不能知道实际对象的大小,引用的对象只能是指针类型。
(2)挪动该类型定义
这个方法很容易想到,你不是说我使用前没声明嘛,那好啊,我直接把它代码搬到你那个#include前不就完了么。也就等于把顺序梳理了下。但这样会代码不大好看。
(3)抽出来放置于公共头文件
这个就相当于给新建了个安置地方,直接就把环给解了。
提前声明的例子:前导声明代替头文件依赖和交叉包含
1.头文件中使用前导声明替代交叉引用,由于前导声明只是一个符号声明,不能知道实际对象的大小,引用的对象只能是指针类型。
2.源文件中包含自己的头文件。
a.h
class B;
class A
{
public:
A();
B* b;
};
b.h
class A;
class B
{
public:
B();
A* a;
};
a.cpp
#include "A.h"
A::A()
{
}
b.cpp
#include "B.h"
B::B()
{
}