在C ++中包含彼此的标题

时间:2008-12-28 11:13:39

标签: c++ recursion header include

我是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;
}

如果它有所不同,我使用的是g ++ 4.3.2。

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

7 个答案:

答案 0 :(得分:65)

通过“宏”,我认为你的意思是#ifndef包括守卫? 如果是这样,#includes肯定会进去。这是包含守卫存在的主要原因之一,因为否则你很容易就会发现无限递归,如你所注意到的那样。

无论如何,问题是当你使用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;
}

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

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

这为编译器提供了必要的信息,即A是一个类。我们还不知道其他任何事情,但由于B只需要包含对它的引用,这就足够了。在A的定义中,我们需要一个B类的成员(不是参考),所以这里B的整个定义必须是可见的。这是幸运的。

答案 1 :(得分:14)

  

一般来说,#include语句应该去哪里?

在包含警卫内部,出于你提到的原因。

对于您的其他问题:您需要至少转发其中一个类,例如像这样:

#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_*/

这仅适用于声明:只要你真正使用 A的实例,你就需要定义它。

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

答案 2 :(得分:4)

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

#ifndef A_H_
#define A_H_

class B;

#include "B.h"

class A
{
    private:
            B b;

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

#endif /*A_H_*/

类似于B级。它编译,但这是最好的方法吗?

答案 3 :(得分:2)

在这种情况下,我创建一个公共标题,包含在具有前向声明的所有源中:

#ifndef common_hpp
#define common_hpp

class A;
class B;

#endif

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

答案 4 :(得分:0)

我怀疑这可以做到。你不是在谈论以递归方式从彼此内部调用两个函数,而是以递归方式将两个对象放在另一个中。想想把一幢带有房子照片的房子放在房子等等......这将占用无限的空间,因为你将拥有无数的房屋和图片。

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

答案 5 :(得分:0)

有些编译器(包括.gcc)也支持 #pragma一次但是你问题中的'包含警卫'习惯是通常的做法。

答案 6 :(得分:0)

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

因此,C ++不会让两个.h文件#include彼此。