头文件包含/转发声明

lia*_*iaK 13 c++ header-files forward-declaration

在我的C++项目中何时必须使用#include "myclass.h"头文件的includes()?我什么时候必须使用class(class CMyClass;)的前向声明?

Cha*_*tie 21

通常首先尝试前进声明.这将减少编译时间等.如果不编译则去#include.如果您需要执行以下任何操作,则必须转到#include:

  1. 访问该类的成员或函数.
  2. 使用指针算术.
  3. 使用sizeof.
  4. 任何RTTI信息.
  5. new/ delete,复制等
  6. 按值使用它.
  7. 继承它.
  8. 有它作为成员.
  9. 函数中的实例.

(来自@Mooing Duck的6,7,8,9)

他们可能更多,但我今天没有得到我的语言法律帽子.

  • 您还需要包含它以按值使用它.因此,如果您继承它,将其作为成员,或在函数中具有实例. (4认同)
  • 为了便于记忆 - 除了 (1) 和 (4) 之外的所有内容都可以从“如果您需要知道`CMyClass`es 实例的大小”中推断出来。 (2认同)

ypn*_*nos 11

如果您只需要指向该类的指针,并且您不需要任何有关该类而​​不是其名称的知识,则可以使用前向声明.


小智 11

转发声明有几个问题:

  • 这就像在多个地方存储您的类名 - 如果您在一个地方更改它,您现在必须在其他地方更改它.重构成为一个挑战,因为代码仍然可以使用更改的类名进行编译,但链接将失败,因为前向声明引用了未定义的类.如果包含头文件但不使用前向声明,则在编译期间将捕获这些问题.
  • 其他人难以维持前瞻性声明.例如,如果头文件包含:

    include "MyProject/MyWidgets/MyFooWidgets/FooUtil/Foo.h"
    
    Run Code Online (Sandbox Code Playgroud)

    而不是前瞻性声明

     class Foo ;  
    
    Run Code Online (Sandbox Code Playgroud)

    其他人很容易找到Foo类声明的位置.有了前瞻性声明,它并不那么明显; 当用户尝试打开变量声明时,某些IDE(如Eclipse)可能会打开前向声明.

  • 当您在包含转发声明的代码中包含头文件但实际代码定义位于您未链接的其他库中时,链接可能会因未定义的符号错误而失败.在编译时捕获此问题更加方便,但是"Could not find file MyProject/MyWidgets/MyFooWidgets/FooUtil/Foo.h"从那时起您将知道在哪里查找相应的错误Foo.cpp并识别包含它的库.

如果您认为您的构建花费的时间太长,那么尝试仅在没有链接的情况下进行编译.如果您的代码编译需要10秒钟,链接需要10分钟,则问题与一些额外的包含无关.同样,如果您的头文件包含很多内容,实际上它会导致性能问题,那么您可能需要时间将该文件的内容重构为多个较小的头文件.

所以什么时候可以转发申报?如果您在与真实声明相同的头文件中执行此操作.

例:

class Foo ;

typedef Foo* FooPtr ;
typedef Foo& FooRef ;

class Foo
{
   public:
      Foo( ) ;
      ~Foo( ) ;
}
Run Code Online (Sandbox Code Playgroud)

要么

class TreeNode ;

class Tree
{
private:
   TreeNode m_root ;
}

class TreeNode
{
   void* m_data ;
} ;
Run Code Online (Sandbox Code Playgroud)

  • 我同意 @David 列出的前向声明问题。但我不同意这个结论:“那么什么时候可以转发声明呢?如果你在与真实声明相同的头文件中进行转发声明。” 正如其他人指出的那样,前向声明是解耦物理布局和加速构建的重要工具。// 我越来越想知道我们是否应该 #include "Foo.hf" - .hf 表示“除了前向声明之外不包含任何内容的头文件”。// 当然,我会自动生成它。 (2认同)

小智 5

作为初学者,当您需要使用它们包含的类型或函数时,您应该始终 #include 头文件 - 不要尝试通过前向声明来“优化”您的构建 - 这几乎没有必要,即使在大型项目中,只要项目架构良好。

唯一绝对需要前向声明的情况是在以下情况下:

struct A {
   void f( B b );
};

struct B {
   void f( A a );
};
Run Code Online (Sandbox Code Playgroud)

其中每个结构(或类)引用另一个结构(或类)的类型。在这种情况下,您需要 B 的前向声明来解决问题:

struct B;   // forward declaration

struct A {
   void f( B b );
};

struct B {
   void f( A a );
};
Run Code Online (Sandbox Code Playgroud)