【问题标题】:Why must we Forward Declare a class and include the corresponding header file in a header file为什么一定要 Forward 声明一个类,并在一个头文件中包含对应的头文件
【发布时间】:2013-08-06 00:30:42
【问题描述】:

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

文件B.h:

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

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

【问题讨论】:

  • 因为你没有给我们一个诚实的例子,而真正的头文件包含包含守卫?
  • @kerrek 是的,它包含警卫。
  • 好吧。为您的问题制作一个代表性示例,我相信您即使不在这里发帖也能弄清楚:-)
  • 请注意,A *a 并没有像您想象的那样声明 A 类型的对象。它声明了一个A* 类型的对象。这样的对象在 C++ 中称为 pointer-to-A

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


【解决方案1】:

以下是基础知识:

  • 对于任何类型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*) 只是该平台上的指针大小(通常在 32 位系统上为 4 字节,在 64 位系统上为 8 字节)。

  • 对于任何类型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 的定义,这是不可能的。

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

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

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


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

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

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

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

希望对您有所帮助。

【讨论】:

  • 我的问题是别的。我问为什么它可以在 cpp 文件中包含头文件并创建类对象,但为什么不能在 hpp 文件中?在 hpp 文件中,我需要 header 和 forward 声明来创建一个类对象。
  • @kalkin:我的回答首先涵盖了基础知识,然后解释了为什么需要什么。它没有具体回答一个。只有了解基础知识,才能回答所有与申报或前向申报有关的问题。
  • 对不起,我没有看到你回答的最后一段。令我困惑的是为什么我还需要前向声明。不包括头文件A.h单独提供A类的完整定义??
  • @kalkin:包含头文件就足够了,除非你有循环引用(即头文件A.h直接或间接包含头文件B.h
  • @kalkin:我不知道头文件中有什么。此外,如果尽管包含头文件,您仍需要提供前向声明,那么这意味着头文件的实现不正确。我怀疑存在循环依赖。
猜你喜欢
  • 2011-07-17
  • 2013-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
相关资源
最近更新 更多