【C/C++】解决“两个类的头文件相互引用/超前引用”问题的方法

一、什么是超前引用?

所谓 超前引用 是指一个类型在定义之前就被用来定义变量和声明函数。一般情况下C/C++要求所有的类型必须在使用前被定义,但是会存在一些特殊情况导致这种要求无法满足,例如

//类A的头文件
#ifndef _A_H_
#define _A_H_
#include "B.h"
class A
{
public:
    int x;
    B b;
}
#enif

//类B的头文件
#ifndef _B_H_
#define _B_H_
#include "A.h"
class B
{
public:
    int y;
    A a;
}
#enif
  • 当一个类 A 中包含另一个类 B 的对象时,则必须在A的源码文件中声明另一个类的头文件;那么如果两个类都互用到了对方的对象,理论上就要互相包含头文件。
  • 如下代码所示,但是这样是无法通过编译的,其原因是它们的头文件互相包含了,你包含我、我又包含你、没完没了!思考一下是不是会出现 a.b.a.b.a.b.a.b.a.b…无穷尽的情况,这种定义方式类同程序中的死循环。
  • 从编译器角度看,编译 A.CPP 时,系统首先定义宏 _A_H_,然后包含 B.h,而 B.h 中的 #include "A.h" 由于 _A_H_ 已经定义,所以不再起作用。在类的声明中,A a 就会让编译器产生 "A" 类型没有定义之类的错误,编译 B.CPP 文件出现的错误可以类似得到。  

二、怎么解决超前引用?

超前引用导致的错误根据不同情况一般有以下几种处理办法:

1、使用类的前置声明

对于所有代码在同一个文件中的情况,在超前引用一个类之前,首先用前置声明说明该标识符是一个类名,即将被超前引用。其使用方法是:

  • a)  用class B;声明即将超前引用的类名
  • b)  定义class ClassA
  • c)  定义class ClassB
  • d)  编写两个类的实现代码。

而一般情况下,Class A和Class B分别有自己的头文件和cpp文件,这种方法需要演变成如下所示,需要注意的是这种方法切记不可使用类名来定义变量和函数的变量参数,只可用来定义引用或者指针。

  • a) 分别定义Class A和Class B,并在cpp文件中实现之
  • b) 在两个头文件的开头分别用class B;和class A;前置声明来声明对方
  • c) 在两个cpp文件中分别包含另外一个类的头文件
//文件A.h中的代码  
//#include "B.h",放到A.cpp中去  
class B;  
  
class A  
{  
public:  
    B* b;  
};  
  
//文件B.h中的代码  
//#include "A.h",放到B.cpp中去  
class A;  
  
class B  
{  
public:  
    A* a;  
};

2、使用全局变量

全局变量可以避免超前引用,将类对象的extern语句加在该类头文件的最后。

3、使用基类指针

这种方法是在必须使用超前引用类的地方一律用基类指针。而一般情况下,两个互相引用的类并不涉及其基类,因此不会造成超前引用。以开始的例子说:在A类中用 Bfather* 代替 B*,或者在B类中用 Afather* 代替 A* 以此来避免出现相互的超前引用。

三、如何避免超前引用

其实上述三种方法并不一定能解决全部的超前引用问题,自己在最近的代码中就遇到了上述三种方法都解决不了的互相引用的问题,最后的解决方法是想办法将A中对于B的引用给去除,根本解决方法还是要尽可能避免超前引用。

那么对于超前引用需要注意哪些问题呢?

  • 不要乱包含头文件,在不需要包含头文件的情况下尽量不要包含。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象或者访问到类的具体成员,那么前置声明就可以解决问题。
  • 对于非库函数头文件,尽量在CPP文件中包含,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并编译成功,那么在A的实现中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)。
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值