C++ 伟大的c++;远期申报混乱
假设我有一个a类和一个B类及其相应的标题: a、 h b、 h 这不起作用: 为了将A的实现编译成一个对象文件,我首先包括A.h,它向前声明A,然后包括b.h,然后声明并定义b。但是定义b时,它不知道A的大小,因此不能将A的对象声明为b的成员 但是,A不需要知道B的大小,因为它只有一个指向B的指针,并且可以在定义B get之前完全定义它。因此,在B被用作成员之前,可以完全知道它的大小,并且完整的声明应该很好 但常识告诉我们,a.c文件应始终如下所示:C++ 伟大的c++;远期申报混乱,c++,member,forward-declaration,C++,Member,Forward Declaration,假设我有一个a类和一个B类及其相应的标题: a、 h b、 h 这不起作用: 为了将A的实现编译成一个对象文件,我首先包括A.h,它向前声明A,然后包括b.h,然后声明并定义b。但是定义b时,它不知道A的大小,因此不能将A的对象声明为b的成员 但是,A不需要知道B的大小,因为它只有一个指向B的指针,并且可以在定义B get之前完全定义它。因此,在B被用作成员之前,可以完全知道它的大小,并且完整的声明应该很好 但常识告诉我们,a.c文件应始终如下所示: #include "a.h" [...]
#include "a.h"
[...]
我真的可以通过在a.c.a.h之前加入b.h来解决这个问题吗?这是否违背了将实现文件的第一行作为其头的include的神圣约定?因为每个类都依赖于另一个,所以两个类都应该在相同的头文件中定义(并且在相同的命名空间中)。如果出于某种原因,它们必须在不同的头文件中,这将起作用 A.h B.h
让我们看看预处理器完成工作后编译器看到了什么:
/* 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
的完整定义来打破循环include循环。大概在A.cpp中,您将#包含B
的完整定义,以便您可以使用其成员。您以向后的方式使用正向声明。代码应该更像这样:
a、 h
b、 h
a.h
不需要知道B
实际上是什么,因为a
包含B*
指针,而不是B
对象。因此a.h
不应该使用#包含“b.h”
,而是应该向前声明b
b.h
确实需要知道A
实际上是什么,因为b
包含A
对象,而不是A*
指针。所以b.h
应该使用#包括“a.h”
,它在定义a
之前已经向前声明b
,然后b.h
完成定义b
a.c
然后可以使用#include“a.h”
引入a
的声明,这样它就可以完成对实现的定义,并且只有当a
的方法需要访问b
的成员时,它才可以使用#include“b.h”
您是否考虑过在定义A之后包含b.h?那么A将失败,因为我假定在创建指向它的指针时b不会定义类型。通常,您在需要声明的文件中提出声明,不要使标头本身包含使用第一个标头的其他标头。您希望使用转发声明消除a.h和b.h之间的循环依赖关系。删除a.h中b.h的包含,并用b的转发声明替换a的转发声明。没有意义在其自身标头中转发声明类(除非你有一个复杂的标题)。非常好的阅读,如果不是完全重复的话:呵呵,你认为这是一个家庭作业,这很好,但最明确的是它不是:-)并且相关的类实际上是应用程序非常不同的子系统的一部分,一个在层次图中定义一个节点,另一个在菜单中重新显示一个想要插入的小部件,但是对于这个问题来说,这将是太多的信息了。@salbeira我希望没有冒犯您的意思。没有人打算这么做。我删除了H这个词,所以我的错误在于我应该在任何包含之前总是在它的头文件中向前声明一个类,因为最终的循环依赖可能想知道一个符号是否引用了一个类型或其他东西。因此,我必须习惯于总是转发声明我使用的类名,而不是为了消除这些错误而提供的,只要我只使用转发类的指针。@salbeira如果我读对了你说的话,那么是的。头文件不应该向前声明它还定义的类(除非头文件定义了相互依赖的多个类)。向前声明任何不需要包含其他头文件就可以解决的问题,让实现文件包含它需要的任何头文件。这减少了标头之间的依赖性,从而减少了更改标头时需要重新编译的代码量。它还可以更好地优化编译器使用预编译头的能力。
#ifndef CLASS_A
#define CLASS_A
class B;
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
#endif
#ifndef CLASS_B
#define CLASS_B
#include "a.h"
class B {
public:
B() : m_a() {}
A m_a;
};
#endif
/* 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;
};
#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
#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
#ifndef CLASS_B
#define CLASS_B
#include "a.h"
/* define class B */
class B {
public:
B() : m_a() {}
A m_a;
};
#endif