我正在寻找当我被允许在另一个类的头文件中做类的前向声明的定义:

我是否可以为基类,为作为成员持有的类,为通过引用传递给成员函数的类,等等这样做?


当前回答

正如Luc Touraille已经很好地解释了在哪里使用和不使用类的前向声明。

我再补充一下为什么我们需要用它。

我们应该尽可能使用Forward声明,以避免不必要的依赖注入。

由于#include头文件被添加到多个文件中,因此,如果我们将一个头文件添加到另一个头文件中,它将在源代码的各个部分添加不必要的依赖注入,这可以通过尽可能将#include头文件添加到.cpp文件中来避免,而不是添加到另一个头文件中,并在头文件.h文件中尽可能使用类前向声明来避免。

其他回答

除了指向不完整类型的指针和引用外,还可以声明函数原型来指定不完整类型的参数和/或返回值。但是,不能定义具有不完整形参或返回类型的函数,除非它是指针或引用。

例子:

struct X;              // Forward declaration of X

void f1(X* px) {}      // Legal: can always use a pointer
void f2(X&  x) {}      // Legal: can always use a reference
X f3(int);             // Legal: return value in function prototype
void f4(X);            // Legal: parameter in function prototype
void f5(X) {}          // ILLEGAL: *definitions* require complete types

我遵循的一般规则是除非必须,否则不包含任何头文件。所以除非我将一个类的对象存储为我的类的成员变量,否则我不会包括它,我只会使用前向声明。

正如Luc Touraille已经很好地解释了在哪里使用和不使用类的前向声明。

我再补充一下为什么我们需要用它。

我们应该尽可能使用Forward声明,以避免不必要的依赖注入。

由于#include头文件被添加到多个文件中,因此,如果我们将一个头文件添加到另一个头文件中,它将在源代码的各个部分添加不必要的依赖注入,这可以通过尽可能将#include头文件添加到.cpp文件中来避免,而不是添加到另一个头文件中,并在头文件.h文件中尽可能使用类前向声明来避免。

只要不需要定义(比如指针和引用),就可以使用前向声明。这就是为什么大多数情况下你会在头文件中看到它们,而实现文件通常会为适当的定义拉出头文件。

我写这篇文章是作为一个单独的回答,而不仅仅是评论,因为我不同意Luc Touraille的回答,不是基于合法性,而是基于健壮的软件和误解的危险。

具体地说,我对您希望界面的用户必须知道的隐含契约有一个问题。

如果您返回或接受引用类型,那么您只是说它们可以传递一个指针或引用,而它们反过来可能只通过前向声明知道这个指针或引用。

当你返回一个不完整的类型X f2();那么你说你的调用者必须拥有x的完整类型规范,他们需要它来在调用站点创建LHS或临时对象。

类似地,如果接受不完整类型,则调用方必须已经构造了作为形参的对象。即使该对象作为函数的另一个不完整类型返回,调用站点也需要完整的声明。例如:

class X;  // forward for two legal declarations 
X returnsX();
void XAcceptor(X);

XAcepptor( returnsX() );  // X declaration needs to be known here

我认为有一个重要的原则,一个头应该提供足够的信息来使用它,而不依赖于其他头。这意味着头文件应该能够包含在编译单元中,而不会在使用它声明的任何函数时引起编译器错误。

除了

如果这种外部依赖是所期望的行为。不使用条件编译,您可以有一个良好的文档要求,要求它们提供自己的声明x的头文件。这是使用#ifdefs的另一种选择,可以是引入模拟或其他变体的有用方法。 重要的区别是一些模板技术,你不需要明确地实例化它们,提到这些只是为了避免有人对我刻薄。