C++ 中相互包含的头文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/396084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:07:30  来源:igfitidea点击:

Headers Including Each Other in C++

c++recursionheaderinclude

提问by Scott

I'm a C++ newbie, but I wasn't able to find the answer to this (most likely trivial) question online. I am having some trouble compiling some code where two classes include each other. To begin, should my #include statements go inside or outside of my macros? In practice, this hasn't seemed to matter. However, in this particular case, I am having trouble. Putting the #include statements outside of the macros causes the compiler to recurse and gives me "#include nested too deeply" errors. This seems to makes sense to me since neither class has been fully defined before #include has been invoked. However, strangely, when I try to put them inside, I am unable to declare a type of one of the classes, for it is not recognized. Here is, in essence, what I'm trying to compile:

我是 C++ 新手,但我无法在网上找到这个(很可能是微不足道的)问题的答案。我在编译一些两个类相互包含的代码时遇到了一些麻烦。首先,我的#include 语句应该放在宏的内部还是外部?在实践中,这似乎无关紧要。但是,在这种特殊情况下,我遇到了麻烦。将#include 语句放在宏之外会导致编译器递归并给我“#include 嵌套太深”错误。这对我来说似乎很有意义,因为在调用 #include 之前还没有完全定义这两个类。然而,奇怪的是,当我尝试将它们放入其中时,我无法声明其中一个类的类型,因为它不被识别。本质上,这是我要编译的内容:

A.h

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

    public:
        A() : b(*this) {}
};

#endif /*A_H_*/

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class B
{
    private:
            A& a;

    public:
        B(A& a) : a(a) {}
 };

#endif /*B_H_*/

main.cpp

主程序

#include "A.h"

int main()
{
    A a;
}

If it makes a difference, I am using g++ 4.3.2.

如果它有所作为,我使用的是 g++ 4.3.2。

And just to be clear, in general, where should #include statements go? I have always seen them go outside of the macros, but the scenario I described clearly seems to break this principle. Thanks to any helpers in advance! Please allow me to clarify my intent if I have made any silly mistakes!

只是要清楚,一般来说,#include 语句应该放在哪里?我一直看到它们在宏之外,但我描述清楚的场景似乎打破了这个原则。提前感谢任何帮助者!如果我犯了任何愚蠢的错误,请允许我澄清我的意图!

回答by jalf

By "the macros" I assume you mean the #ifndef include guards? If so, #includes should definitely go inside. This is one of the major reasons why include guards exists, because otherwise you easily end up with an infinite recursion as you noticed.

通过“宏”,我假设您的意思是#ifndef 包含警卫?如果是这样,#includes 绝对应该进入。这是包含守卫存在的主要原因之一,否则你很容易像你注意到的那样以无限递归结束。

Anyway, the problem is that at the time you use the A and B classes (inside the other class), they have not yet been declared. Look at what the code looks like after the #includes have been processed:

无论如何,问题是当您使用 A 和 B 类(在另一个类中)时,它们还没有被声明。看看处理完#includes 后的代码是什么样子的:

//#include "A.h" start
#ifndef A_H_
#define A_H_

//#include "B.h" start
#ifndef B_H_
#define B_H_

//#include "A.h" start
#ifndef A_H_ // A_H_ is already defined, so the contents of the file are skipped at this point
#endif /*A_H_*/

//#include "A.h" end

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

//#include "B.h" end

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/
//#include "A.h" end

int main()
{
    A a;
}

Now read the code. B is the first class the compiler encounters, and it includes an A&member. What is A? The compiler hasn't encountered any definition of Ayet, so it issues an error.

现在阅读代码。B 是编译器遇到的第一个类,它包含一个A&成员。什么是A?编译器还没有遇到任何的定义A,所以它会发出错误。

The solution is to make a forward declaration of A. At some point before the definition of B, add a line class A;

解决方案是对A做一个前向声明。在B的定义之前的某个点,添加一行 class A;

This gives the compiler the necessary information, that A is a class. We don't know anything else about it yet, but since B only needs to include a reference to it, this is good enough. In the definition of A, we need a member of type B (not a reference), so here the entire definition of B has to be visible. Which it is, luckily.

这为编译器提供了必要的信息,即 A 是一个类。我们对此一无所知,但由于 B 只需要包含对它的引用,这就足够了。在 A 的定义中,我们需要一个 B 类型的成员(不是引用),所以这里 B 的整个定义必须是可见的。幸运的是,它是什么。

回答by Konrad Rudolph

And just to be clear, in general, where should #include statements go?

只是要清楚,一般来说,#include 语句应该放在哪里?

Inside the include guards, for the reason you mentioned.

由于您提到的原因,在包括守卫内部。

For your other problem: you need to forward-declare at least one of the classes, e.g. like this:

对于您的另一个问题:您需要提前声明至少一个类,例如:

#ifndef B_H_
#define B_H_

// Instead of this:
//#include "A.h"

class A;

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

This only works for declarations though: as soon as you really usean instance of A, you need to have defined it as well.

但这仅适用于声明:一旦您真正使用的实例A,您还需要定义它。

By the way, what Nathan says is true: you can't put class instances into each other recursively. This only works with pointers(or, in your case, references) to instances.

顺便说一句,Nathan 说的是真的:您不能将类实例递归地放入彼此中。这仅适用于指向实例的指针(或者,在您的情况下,是引用)。

回答by Scott

Oops! I think I found a solution that involves putting the #include statements inside the class and using a forward declaration. So, the code looks like:

哎呀!我想我找到了一个解决方案,涉及将 #include 语句放在类中并使用前向声明。所以,代码看起来像:

#ifndef A_H_
#define A_H_

class B;

#include "B.h"

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/

And similarly for class B. It compiles, but is this the best approach?

和 B 类类似。它可以编译,但这是最好的方法吗?

回答by DarenW

In such situations, i create a common header to be included in all sources with forward declarations:

在这种情况下,我创建了一个通用标头,以包含在所有带有前向声明的源中:

#ifndef common_hpp
#define common_hpp

class A;
class B;

#endif

Then the individual class header files typically don't need any #includes to reference other classes, if all that's needed are pointers or references to those classes. Half the time though there are other goodies in those headers, but at least any problem with circular references are solved with common.hpp

然后单个类头文件通常不需要任何 #includes 来引用其他类,如果只需要指向这些类的指针或引用。一半的时间虽然这些头文件中还有其他好东西,但至少循环引用的任何问题都可以通过 common.hpp 解决

回答by Alex Spencer

Dependency between two classes in a good software design can be drawn as a tree.

一个好的软件设计中两个类之间的依赖关系可以绘制成一棵树。

For this reason C++ won't let two .h files #include each other.

出于这个原因,C++ 不会让两个 .h 文件相互#include。

回答by frankodwyer

Some compilers (inc. gcc) also support #pragma oncehowever the 'include guards' idiom in your question is the usual practice.

一些编译器(包括 gcc)也支持#pragma once但是你的问题中的“包含守卫”习语是通常的做法。

回答by Nathan Fellman

I doubt that this can be done. You're not talking about calling two functionsfrom inside each other recursively, but rather of putting two objectsone inside the other recursively. Think about putting a house with a picture of a house with a picture of a house etc... This will take up an infinite amount of space because you'll have an infinite number of houses and pictures.

我怀疑这能不能做到。您不是在谈论从彼此内部递归调用两个函数,而是将两个对象递归地放在另一个内部。想想把房子和房子的图片放在房子的图片上……这将占用无限量的空间,因为你将拥有无限数量的房子和图片。

What you cando is have each of Aand Binclude either pointers or references to each other.

可以做的是让每个AB包含彼此的指针或引用。