伟大的c ++前沿宣言混乱

时间:2018-02-07 02:01:41

标签: c++ member forward-declaration

考虑我有一个A类和一个B类及其相应的标题:

A.H

#ifndef CLASS_A
#define CLASS_A

/* forward declare A */
class A;
/* includes */
#include "b.h"
/* define class A */
class A {
public:
    A() : p_b(nullptr) {}
    B *p_b;
};
#endif

b.h

#ifndef CLASS_B
#define CLASS_B

/* forward declare B */
class B;
/* includes */
#include "a.h"
/* define class B */
class B {
public:
    B() : m_a() {}
    A m_a;
};
#endif

这不起作用:

要将A的实现编译成一个对象文件,我首先包括啊,前向声明A然后包含bh然后声明并定义B.但是当定义B时它不知道A的大小因此不能将A的对象声明为B的成员。

然而,

A不需要知道B的大小,因为它只有一个指向B的指针,可以在B定义之前完全定义。因此B的大小在被用作成员之前可以完全知道,完整的声明应该没问题。

常识虽然告诉我们a.c文件应该总是这样:

#include "a.h"

[...]

我可以通过在a.c中的a.h之前包含b.h来实际解决问题吗?这是否反对某个实现文件的第一行包含它的标题的神圣惯例?

3 个答案:

答案 0 :(得分:4)

您正在以向后的方式使用前向声明。代码看起来应该更像这样:

A.H

#ifndef CLASS_A
#define CLASS_A

/* forward declare B */
class B;

/* define class A */
class A {
public:
    A() : p_b(nullptr) {}
    B *p_b;
};

#endif

b.h

#ifndef CLASS_B
#define CLASS_B

#include "a.h"

/* define class B */
class B {
public:
    B() : m_a() {}
    A m_a;
};

#endif

a.h并不需要知道B实际上是什么,因为A包含B*指针而不是B对象。因此a.h根本不应该使用#include "b.h",而是应该向前声明B

b.h确实需要知道A实际上是什么,因为B包含A对象而不是A*指针。因此b.h应该使用#include "a.h",在定义B之前已经转发声明A,然后b.h完成定义B

a.c然后可以使用#include "a.h"引入A声明,以便它可以完成对实现的定义,并且只有#include "b.h"才能使用A方法需要访问B的成员。

答案 1 :(得分:1)

让我们看一下编译器在预处理器完成它之后看到的内容:

/* forward declare A */
class A;
/* includes */
/* forward declare B */
class B;
/* includes */
/* define class B */
class B {
public:
    B() : m_a() {}
    A m_a;
};
/* define class A */
class A {
public:
    A() : p_b(nullptr) {}
    B *p_b;
};

正如您所看到的,B的类定义在A完全定义之前出现,因此您的程序格式不正确。

在这里,你只需要一个前向声明(B),它应该出现在A.h中A的定义之前:

#ifndef CLASS_A
#define CLASS_A

// Forward declare B so that B* p_b is legal
class B;

// Note that B.h is *not* included here

class A {
public:
    A() : p_b(nullptr) {}
    B *p_b;
};
#endif

在这里,您通过向前声明B而不是包含B的完整定义来打破循环包含周期。据推测,在A.cpp中,您将#include B的完整定义,以便您可以使用其成员。

答案 2 :(得分:0)

由于每个类都依赖于另一个类,因此两个类应该在同一个头文件中定义(并且在同一名称空间中)。如果由于某种原因它们必须位于不同的头文件中,这将起作用。

A.H

#ifndef CLASS_A
#define CLASS_A

class B;

class A {
public:
    A() : p_b(nullptr) {}
    B *p_b;
};
#endif

B.h

#ifndef CLASS_B
#define CLASS_B

#include "a.h"

class B {
public:
    B() : m_a() {}
    A m_a;
};
#endif