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

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


当前回答

把你自己放在编译器的位置:当你转发声明一个类型时,编译器只知道这个类型存在;它对自己的规模、成员或方法一无所知。这就是为什么它被称为不完全类型。因此,不能使用类型来声明成员或基类,因为编译器需要知道类型的布局。

假设有以下正向声明。

class X;

以下是你能做的和不能做的。

使用不完整类型可以做什么:

将成员声明为指向不完整类型的指针或引用: 类Foo { X * p; X设计验证; }; 声明接受/返回不完整类型的函数或方法: 无效的f1 (X); X f2 (); 定义接受/返回指向不完整类型(但不使用其成员)的指针/引用的函数或方法: void f3(X*, X&) {} X& f4() {} X* f5() {}

不完整类型不能做的事情:

Use it as a base class class Foo : X {} // compiler error! Use it to declare a member: class Foo { X m; // compiler error! }; Define functions or methods using this type void f1(X x) {} // compiler error! X f2() {} // compiler error! Use its methods or fields, in fact trying to dereference a variable with incomplete type class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };


对于模板,没有绝对的规则:是否可以使用不完整类型作为模板形参取决于模板中使用该类型的方式。

例如,std::vector<T>要求其参数为完整类型,而boost::container::vector<T>则不要求。有时,只有在使用某些成员函数时才需要完整类型;例如,std::unique_ptr<T>就是这种情况。

一个文档完备的模板应该在其文档中指出其参数的所有需求,包括它们是否需要为完整类型。

其他回答

把你自己放在编译器的位置:当你转发声明一个类型时,编译器只知道这个类型存在;它对自己的规模、成员或方法一无所知。这就是为什么它被称为不完全类型。因此,不能使用类型来声明成员或基类,因为编译器需要知道类型的布局。

假设有以下正向声明。

class X;

以下是你能做的和不能做的。

使用不完整类型可以做什么:

将成员声明为指向不完整类型的指针或引用: 类Foo { X * p; X设计验证; }; 声明接受/返回不完整类型的函数或方法: 无效的f1 (X); X f2 (); 定义接受/返回指向不完整类型(但不使用其成员)的指针/引用的函数或方法: void f3(X*, X&) {} X& f4() {} X* f5() {}

不完整类型不能做的事情:

Use it as a base class class Foo : X {} // compiler error! Use it to declare a member: class Foo { X m; // compiler error! }; Define functions or methods using this type void f1(X x) {} // compiler error! X f2() {} // compiler error! Use its methods or fields, in fact trying to dereference a variable with incomplete type class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };


对于模板,没有绝对的规则:是否可以使用不完整类型作为模板形参取决于模板中使用该类型的方式。

例如,std::vector<T>要求其参数为完整类型,而boost::container::vector<T>则不要求。有时,只有在使用某些成员函数时才需要完整类型;例如,std::unique_ptr<T>就是这种情况。

一个文档完备的模板应该在其文档中指出其参数的所有需求,包括它们是否需要为完整类型。

在文件中,只使用指向类的指针或引用。指针/引用不能调用任何成员/成员函数。

//前向声明

我们可以声明类型为Foo*或Foo&的数据成员。

我们可以声明(但不能定义)带有Foo类型的参数和/或返回值的函数。

可以声明类型为Foo的静态数据成员。这是因为静态数据成员定义在类定义之外。

我只是想补充一件重要的事情,你可以用Luc Touraille的回答中没有提到的转发类来做。

使用不完整类型可以做什么:

定义接受/返回的函数或方法 指向不完整类型的指针/引用并转发该指针/引用 到另一个函数。

void  f6(X*)       {}
void  f7(X&)       {}
void  f8(X* x_ptr, X& x_ref) { f6(x_ptr); f7(x_ref); }

模块可以将前向声明类的对象传递给另一个模块。

假定前向声明将使代码得到编译(obj被创建)。但是,除非找到定义,否则链接(exe创建)将不会成功。

Lakos区分类的使用

In-name-only(向前声明就足够了)和 In-size(为此需要类定义)。

我从来没见过比这更简洁的发音:)