5

嗨,我注意到如果我在 a 中包含一个头文件,.cpp那么我可以创建该头文件类的一个对象。就像如果我包括在内A.hmain.cpp那么我可以写A *a;main.cpp. 但是,如果我在另一个头文件中包含一个头文件,然后尝试创建包含的头文件的对象,这将不起作用。像,

文件B.h

#include "A.h"
class B
{
public:
    B(){};
    A *a;
};

我必须添加类的前向声明A才能使其工作。为什么?

4

1 回答 1

15

以下是基础知识:

  • 对于任何类型A,如果你声明了一个 , , , 等类型的变量A&A*那么A**编译A***不需要知道A变量声明处的完整定义。它只需要知道A一个类型;这就对了。所以一个前向声明就足够了:

    class A; //forward declaration
    
    class B
    {
       A * pA;  //okay - compiler knows A is a type
       A & refA;/ okay - compiler knows A is a type
    };
    

    不需要 完整的定义,因为编译器仍然可以计算sizeof(B)哪个反过来依赖于sizeof(A*)sizeof(A&)- 这些对于编译器来说是已知的,即使它不知道 sizeof(A)。请注意,这sizeof(A*)只是该平台上的指针大小(通常 4是 32 位系统上的8字节或 64 位系统上的字节)。

  • 对于任何类型A,如果你声明了一个类型为 , 等的变量AA[N]那么A[M]N]编译器需要A在变量声明的地方知道类型的完整定义。在这种情况下,前向声明是不够的。

    class A; //forward declaration
    class B
    {
       A a;  //error - the compiler only knows A is a type
             //it doesn't know its size!
    };
    

    但这是正确的:

    #include "A.h" //which defines A
    
    class B
    {
       A a;  //okay
    };
    

    需要完整的定义 以便编译器可以计算,如果编译器不知道 的定义,这是不可能的。sizeof(A)A

    请注意,类的定义是指“类成员、它们的类型以及类是否具有虚函数的完整规范”。如果编译器知道这些,它可以计算类的大小。

了解这些基础知识后,您可以决定是否将标头包含到其他标头中,或者仅前向声明就足够了。如果前向声明就足够了,那是您应该选择的选项。仅在需要时才包含标题。

但是,如果你在头文件中提供前向声明AB.h那么你必须将头文件包含A.h在它的实现文件中BB.cpp因为在的实现文件中B,你需要访问A编译器需要完整定义的成员。 A. 同样,仅需要访问. A:-)


对不起,我没有看到你答案的最后一段。令我困惑的是为什么我还需要前向声明。不包括头文件Ah单独提供了A类的完整定义吗?——</p>

我不知道头文件中有什么。另外,如果尽管包含了头文件,但还需要提供前向声明,则意味着头文件实现不正确。我怀疑存在循环依赖

  • 确保没有两个头文件相互包含。例如,如果A.h包含B.h,则B.h不得A.h直接或间接包含。

  • Use forward declaration and pointer-declaration to break such circular dependency. The logic is pretty much straight-forward. If you cannot include A.h in B.h, which implies you cannot declare A a in B.h (because for this, you have to include the header A.h also). So even though you cannot declare A a, you can still declare A *pA, and for this a forward declaration of A is enough. That way you break the circular dependency.

Hope that helps.

于 2013-01-19T10:45:25.583 回答