18

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

4

5 回答 5

28

作为一项规则,首先尝试前向声明。这将减少编译时间等。如果不能编译,请使用#include. 如果您需要执行以下任何操作,则必须使用#include:

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

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

他们可能更多,但我今天还没有戴上我的语言法帽子。

于 2010-05-14T08:24:22.983 回答
11

如果你只需要一个指向类的指针并且不需要任何关于类的知识而不是它的名字,你可以使用前向声明。

于 2010-05-14T08:18:51.717 回答
11

转发声明有几个问题:

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

    include "MyProject/MyWidgets/MyFooWidgets/FooUtil/Foo.h"
    

    而不是前向声明

     class Foo ;  
    

    其他人很容易找到声明类 Foo 的位置。使用前向声明,它并不那么明显;当用户尝试打开变量的声明时,Eclipse 等一些 IDE 可能会打开前向声明。

  • 当您在代码中包含包含前向声明的头文件但实际代码定义位于您未链接的某个其他库中时,链接可能会失败并出现未定义的符号错误。在编译时发现这个问题会更方便,"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( ) ;
}

或者

class TreeNode ;

class Tree
{
private:
   TreeNode m_root ;
}

class TreeNode
{
   void* m_data ;
} ;
于 2012-01-13T18:25:44.640 回答
4

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

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

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

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

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

struct B;   // forward declaration

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

struct B {
   void f( A a );
};
于 2010-05-14T08:27:09.857 回答
4

您应该努力最小化您#include的 s 以减少编译时间,同时也有助于模块化和可测试性。正如@ypnos 所说,当您只需要指针时,类转发非常好。

有关如何减少标头依赖关系的一些实用技巧,请参见例如这篇文章

于 2010-05-14T08:27:43.413 回答