C++ 前置声明

问题

最近遇到了两个类A、B相互调用的情况,于是想当然的在两个类A和B的头文件中 #include 了所需的头文件,当然结果编译报错了。为什么呢,A需要B,B需要A,形成了循环,违反了程序的确定性原则。代码如下图所示:
在这里插入图片描述
如这样相互包含的问题,可以使用前置声明来解决。即:在头文件中声明该类,在实现文件中包含该类。如下图所示:
在这里插入图片描述

解析

为什么这样使用前置声明,即在AAA.h中声明class BBB; 在BBB.h中声明class AAA; 且成员变量写为所声明类的指针变量, 便不会产生相互包含的错误呢?

原因在于:class BBB;这种方式仅仅是一种符号声明,告诉编译器存在BBB这个类,不会去确定BBB这个类的所占资源(内存)大小和这个类的实现。

上图中可以看到在AAA.h中定义的是BBB的指针变量或引用变量,而不是普通的BBB变量,这是因为定义指针变量或引用变量,编译器只需给该变量分配4字节(32位程序)内存,而不用管BBB对象具体需要占用多少内存,也不去确定该类的构造函数是如何实现的,这些事情是在创建该对象(即AAA.cpp中:b = new BBB;)时才会去确定;

但是若定义普通的BBB变量:BBB b; 的话,编译器需要知道b变量占用了多大内存,构造函数如何实现,然后计算需要为AAA类分配内存大小,这样只是声明Class BBB;就不行了,否则会报错:“使用了未定义的类BBB”,解决办法是#include"BBB.h”,又回到了开始的问题。

同理,BBB.h中关于AAA的声明及变量定义也是如此。

优点

使用前置变量,即:在头文件中声明该类,在实现文件中包含该类。有其一定的优势,以AAA类为例:
(1)BBB修改后重新编译的话,因为是前置声明,所以不需要重新编译AAA.h;
(2)定义的成员变量为指针变量或引用变量,内存固定增加了4(32位程序),减少了AAA类的内存占用大小,这体现在STL的容器里包含是类的对象还是指针的时候特别有用。

附录

这里附上成员变量声明为"引用变量"时的用法代码:

// AAA.h
#pragma once

class BBB;
class AAA {
public:
	AAA(BBB &x);
	~AAA();

private:
	BBB &b;
};

// AAA.cpp
#include "AAA.h"

AAA::AAA(BBB &x) : b(x)
{
}

AAA::~AAA(void)
{
}

// BBB.h
#pragma once

class AAA;
class BBB {
public:
	BBB(void);
	~BBB(void);
	void NewAAA();

private:
	AAA *a;
};

// BBB.cpp
#include "BBB.h"
#include "AAA.h"

BBB::BBB()
{
	NewAAA();
}

BBB::~BBB(void)
{
}

void BBB::NewAAA()
{
 BBB b;
 a = new AAA( b );
}

// main.cpp
#include <iostream>

int main()
{
	return 0;
}

[补] 前置声明和 #include 的优缺点

前置声明(forward declaration) 是类、函数和模板的纯粹声明,没伴随着其定义。

  • 优点:
    1. 前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。
    2. 前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。
  • 缺点:
    1. 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。
    2. 前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API。 例如扩大形参类型,加个自带默认参数的模板形参等等。
    3. 前置声明来自命名空间 std:: 的 symbol 时,其行为未定义(在 C++11 标准规范中明确说明)。
    4. 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。
    5. 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂。
    6. 很难判断什么时候该用前置声明,什么时候该用 #include ,某些场景下面前置声明和 #include 互换以后会导致意想不到的结果。

所以我们尽可能避免使用前置声明,而是使用 #include 头文件来保证依赖关系。

参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值