为什么我们必须转发声明一个类并在头文件中包含相应的头文件

时间:2013-01-19 10:33:03

标签: c++ class header-files forward-declaration

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

档案B.h

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

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

1 个答案:

答案 0 :(得分:13)

以下是基础知识:

  • 对于任何类型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字节。

  • 对于任何类型A,如果声明类型为A[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,如果编译器不知道{{1的定义,则不可能}}。

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

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

但是,如果您在标题A中提供B.h的前瞻性声明,则必须在A.h的实施文件中包含头文件B,即{ {1}},因为在B.cpp的实现文件中,您需要访问编译器需要B完整定义的A成员。好了,只有当需要访问 A成员时才包含。 : - )


  

抱歉,我没有看到你答案的最后一段。令我困惑的是为什么我也需要前瞻性声明。不包括头文件A.h单独提供A类的完整定义? -

我不知道头文件中有什么内容。此外,如果尽管包含头文件,您还需要提供前向声明,然后它暗示标头实现不正确。我怀疑存在循环依赖

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

  • 使用前向声明和指针声明来打破这种循环依赖。逻辑非常简单。如果您不能在A.h中加入A.h,则表示您无法在B.h中声明A a(因为为此,您还必须包含标题B.h) 。因此,即使您无法声明A.h,您仍然可以声明A a,为此,A *pA的前向声明就足够了。这样就可以打破循环依赖。

希望有所帮助。