考虑我有一个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来实际解决问题吗?这是否反对某个实现文件的第一行包含它的标题的神圣惯例?
答案 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