c++头文件相互包含

Posted

技术标签:

【中文标题】c++头文件相互包含【英文标题】:c++ header files including each other mutually 【发布时间】:2012-01-21 13:06:25 【问题描述】:

我有两个类都定义在单独的头文件中。每个文件都有一个属于其他类类型的字段。现在我在每个文件的标题中包含了其他文件的标题,但是编译器正在生成错误。我错过了什么?

【问题讨论】:

【参考方案1】:

你不能让每个类都有“一个属于其他类类型的字段”;那将是一个递归定义,不仅编译器无法理解它,它甚至没有逻辑意义。

每个类都有一个字段是另一个类的类型,这是你只能在 M.C. 中看到的那种不可能。埃舍尔的画作,或其动画,像这样的:

 

                               

              B. de Smit 和 H. W. Lenstra - 来源:escherdroste.math.leidenuniv.nl

                  基于 Escher 的“印刷画廊”平版画,1956 年,see Wikipedia

 

这两个字段中的一个必须是指针,以打破递归包含,并避免逻辑上的不可能性。

这就引出了下一个问题:如果类 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;

【讨论】:

Animation source @Potatoswatter 非常感谢!我修改了图片的标题。 图片在这里完美!【参考方案2】:

您不应该将头文件包含在其他头文件中,只需将头文件包含在源文件中即可。

在标题中你可以使用前向声明:

// In Class1.h
class Class2;

// In class2.h
class Class1;

您还可以使用预处理器防止文件被包含两次:

// Class1.h
#ifndef __CLASS_1_H
#define __CLASS_1_H

// content

#endif

【讨论】:

注意:前向声明意味着您只能将该类用作头文件中的指针。不是类实例。 也可以在函数声明中作为返回类型或参数类型,作为引用类型的一部分。 您不应该使用保留名称作为包含守卫(或其他任何东西),否则您可能会遇到像this 这样的问题。 @GregorBrandt 所以如果我不能将它用作实例,那么这里的所有东西都是无用的【参考方案3】:

我知道这是一个老话题,但也许你仍然对解决方案感兴趣!

实际上,在 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;

仅此而已!

当然这只是出于好奇:)

【讨论】:

你试过编译吗? MSVC 给了我错误:"B&lt;A&gt;::a" uses an undefined class "A" 顺便说一句,这并不意味着在生产代码中使用。这只是为了好玩,这就是为什么不标记为答案。【参考方案4】:

你可能想使用前向声明,除非你真的想把每个类的实例放在一起。在这种情况下,你不应该使用任何东西。

【讨论】:

【参考方案5】:

如果 B 只能存在于 A 中,我似乎能够在不使用指针的情况下创建 A 和 B。 B 必须简单地转发声明 A 而不是包含它(避免递归包含)。

在我的例子中,Document 有一个 Section,它会引用它的 Document

section.h

class Document;

class Section

    public:
        Section(Document& document) : documentdocument  
    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 可能会在其他情况下启用它,但我不确定可读性...

【讨论】:

引用是作为指针实现的,所以本质上你做的事情和指针一样。【参考方案6】:

除了前向声明的可能性之外 - 如果您似乎需要两个类相互包含在另一个类中,那么根据我的经验,这是继承深度错误的标志。 Eather 这些类是一种兄弟姐妹,您应该为两者创建一个父类。 或者您正在尝试使用一个实际上是父类的类,该类应该具有该父类的兄弟。然后你应该创建这个兄弟作为第三类。

【讨论】:

以上是关于c++头文件相互包含的主要内容,如果未能解决你的问题,请参考以下文章

qt c++对象头文件如何相互包含

在 Objective-C 中使用外部 C++ 头文件

c++ cout需要包含哪个头文件?

c++关于multiset的头文件包含问题。

我需要相互包含两个头文件,而不是使用前向声明导致“不完整类型”错误

C++中使用Thread类需要包含啥头文件