循环包含依赖项/转发声明

Circular include dependency/Forward Declarations

本文关键字:转发 声明 包含 依赖 循环      更新时间:2023-10-16

我看到的所有循环包含依赖关系的解决方案都只是说,在"这种特殊情况"下,完整的类定义是不必要的,因为"您"只使用指向该类的指针。

我遇到了这个问题,并使用前向声明修复了它。

我想知道当你需要两个类中另一个类的具体定义时,你应该怎么做。

此外,为什么使用指向类的指针可以使用正向声明而不是类定义?

在什么情况下,您需要为两个类预先知道规范?

一个不可能的情况是:

class A
{
B m_b;
};
class B
{
A m_a;
};

但这是不可能的,因为类A的大小取决于类B的大小,但类B的尺寸取决于类A的尺寸。当你试图构造其中一个时,你也会得到一个无限级数A myA; myA.m_b.m_a.m_b.m_a....

如果你使用指针,你不需要知道任何一个的大小;指针的大小总是相同的,这取决于你所在的平台。而且这个系列会消失,因为堆中的对象需要显式创建。

我想知道当您需要特定的两个类中另一个类的定义。

在现代编译器中,它可以通过前向声明和延迟定义来完成。许多较旧的编译器只允许指针&对前向声明类型的引用。

这里有一个人为的例子:

A.hpp

class B;
class A
{
public:
int32_t Value;
A(int32_t value) : Value(value) { }
int32_t Add(B b) const;
}

B.hpp

#include "A.hpp"
class B
{
public:
int32_t Value;
B(int32_t value) : Value(value) { }
int32_t Sub(A a) const;
}

AB.hpp

#include "A.hpp"
#include "B.hpp"
inline int32_t A::Add(B b) const
{
return this->Value + b.Value;
}
inline int32_t B::Sub(A a) const
{
return this->Value - a.Value;
}

此外,为什么使用指向类的指针可以使用forward声明而不是类定义?

Forward声明只是编译器的名称。该概念存在,因此您可以使用尚未定义的类型。这是必要的,因为C++解析代码的方式,它继承了大量C语言的工件。C++解析器实际上只是仅向前的文本处理器,在#include和使用宏时注入文本。这是一个概念简单的模型,使C/C++编译器在早期更容易编写。与之相比,C#/Java只需使用/inport,就可以用简单的语法在类之间愉快地创建循环依赖关系。

指针实际上只是整数,类似于shortint,但具有由语言强制执行的特殊语义,并且在编译时基于CPU架构已知固定大小。这使得指针声明对于编译器来说处理起来非常简单。

正向声明方便了循环依赖关系和实现隐藏(这也恰好加快了编译时间(。想想皮条客这个成语吧。如果没有前向声明,就没有类型安全的方法来隐藏实现细节。