我有两个类都在单独的头文件中定义。每个文件都有一个属于其他类类型的字段。现在我在每个文件的标头中包含其他文件的标头,但编译器正在生成错误。我错过了什么?
你不能让每个类都有“一个属于其他类类型的字段”;这将是一个递归定义。编译器将无法理解它,甚至没有逻辑意义:它将导致无限大的数据结构。 M.C. 的“Print Gallery”版画可以说明逻辑上的不可能性。埃舍尔,或者更好的是,通过这个令人敬畏的石版画动画:
B. de Smit 和 H. W. Lenstra 制作的动画 - 来源:escherdroste.math.leidenuniv.nl
基于埃舍尔的“Print Gallery”平版画,1956 年,参见维基百科
(动画显示了一个港口,其中包含一座建筑物,其中包含一幅画,其中包含一个港口,其中包含一座建筑物......无穷无尽。)
这两个字段之一必须是指针(或引用,只不过是一个不可变的指针),以便打破递归包含,并避免逻辑不可能性。
这给我们带来了下一个问题:
如果类B要包含类A的实例,那么显然,A必须在类B之前声明,以便编译器在编译B时已经知道A。
但是如果类 A 在类 B 之前声明,我们如何在 A 中声明指向 B 的指针呢?在编译 A 时,B 类还不知道!
这个问题的答案是一个称为“前向声明”的特殊结构,它的存在正是为了适应这种情况。 B 类的前向声明如下所示:
class B;
它只是告诉编译器将会有一个名为 B 的类。它不会告诉编译器有关类 B 的内容的任何信息,因此我们对它无能为力,但我们可以做一件事:声明指向 B 的指针。
所以,问题的完整解决方案如下所示:
(注意:为了简单起见,我省略了显示标头防护装置。通常,它们很重要,但在这种情况下,它们与当前的问题无关。)
文件“A.h”:
/* This is called a "forward declaration". We use it to tell the compiler that
the identifier "B" will from now on stand for a class, and this class will be
defined later. We will not be able to make any use of "B" before it has been
defined, but we will at least be able to declare pointers to it. */
class B;
class A
{
/* We cannot have a field of type "B" here, because it has not yet been
defined. However, with the forward declaration we have told the compiler
that "B" is a class, so we can at least have a field which is a pointer
to "B". */
B* pb;
}
文件“B.h”:
#include "A.h"
class B
{
/* the compiler now knows the size of "A", so we can have a field of type "A". */
A a;
}
在标头中,您可以使用前向声明:
// In Class1.h
class Class2;
// In class2.h
class Class1;
您还可以使用预处理器防止文件被包含两次:
// Class1.h
#ifndef __CLASS_1_H
#define __CLASS_1_H
// content
#endif
实际上,在 C++ 中,您可以递归地使用两个类而不使用指针,这里是如何做到这一点。
文件:a.h
#include <b.h>
class A {
B<> b;
}
文件:b.h
class A;
template<typename T = A>
class B {
T a;
}
文件:main.cpp
#include "a.h"
A a;
仅此而已!
当然这只是出于好奇:)
在我的例子中,
Document
有一个
Section
,它获取对其Document
的引用。section.h
class Document;
class Section
{
public:
Section(Document& document) : document{document} {}
private:
Document& document;
};
document.h
#include "section.h"
class Document
{
public:
Document() : section{*this} {}
private:
Section section;
};
main.cpp
#include "document.h"
int main()
{
Document document{};
}
此代码使用
g++
编译并在 Linux 上运行。
一组(复杂)
ifdef
可能可以在其他情况下启用它,但我不确定可读性......