我正在寻找允许在另一个类的头文件中进行类的前向声明的定义:
我是否允许为基类,作为成员持有的类,通过引用传递给成员函数的类等执行此操作?
让自己处于编译器的位置:当你转发声明一个类型时,所有编译器都知道这个类型存在; 它对它的大小,成员或方法一无所知.这就是为什么它被称为不完整类型.因此,您不能使用该类型来声明成员或基类,因为编译器需要知道该类型的布局.
假设以下前瞻性声明.
class X;
这是你能做什么,不能做的.
你可以用不完整的类型做什么:
将成员声明为指针或对不完整类型的引用:
class Foo { X *p; X &r; };
声明接受/返回不完整类型的函数或方法:
void f1(X); X f2();
定义接受/返回指向不完整类型的指针/引用的函数或方法(但不使用其成员):
void f3(X*, X&) {} X& f4() {} X* f5() {}
你不能用不完整的类型做什么:
将它用作基类
class Foo : X {} // compiler error!
用它来声明一个成员:
class Foo { X m; // compiler error! };
使用此类型定义函数或方法
void f1(X x) {} // compiler error! X f2() {} // compiler error!
使用其方法或字段,实际上尝试取消引用具有不完整类型的变量
class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };
对于模板,没有绝对规则:是否可以使用不完整类型作为模板参数取决于模板中使用类型的方式.
例如,std::vector
要求其参数为完整类型,而boost::container::vector
不是.有时,只有在使用某些成员函数时才需要完整类型; 例如,情况就是如此std::unique_ptr
.
记录良好的模板应在其文档中指出其参数的所有要求,包括它们是否需要完整类型.
主要规则是,您只能转发声明其内存布局(以及成员函数和数据成员)不需要在您转发声明的文件中知道的类.
这将排除基类和除引用和指针使用的类之外的任何东西.
Lakos区分了课堂使用
仅限名称(前向声明就足够了)和
in-size(需要类定义).
我从来没有见过它更简洁明了:)
除了指向不完整类型的指针和引用之外,您还可以声明指定参数和/或返回不完整类型值的函数原型.但是,除非是指针或引用,否则无法定义具有不完整的参数或返回类型的函数.
例子:
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
到目前为止,没有一个答案描述何时可以使用类模板的前向声明.所以,在这里.
可以将类模板转发声明为:
templatestruct X;
遵循接受的答案的结构,
这是你能做什么,不能做的.
你可以用不完整的类型做什么:
将成员声明为指针或对另一个类模板中不完整类型的引用:
templateclass Foo { X * ptr; X & ref; };
声明一个成员是一个指针或对其不完整实例之一的引用:
class Foo { X* ptr; X & ref; };
声明接受/返回不完整类型的函数模板或成员函数模板:
templatevoid f1(X ); template X f2();
声明接受/返回其不完整实例化的函数或成员函数:
void f1(X); X f2();
定义接受/返回指向不完整类型的指针/引用的函数模板或成员函数模板(但不使用其成员):
templatevoid f3(X *, X &) {} template X & f4(X & in) { return in; } template X * f5(X * in) { return in; }
定义接受/返回其不完整实例之一(但不使用其成员)的指针/引用的函数或方法:
void f3(X*, X &) {} X & f4(X & in) { return in; } X * f5(X * in) { return in; }
将它用作另一个模板类的基类
templateclass Foo : X {} // OK as long as X is defined before // Foo is instantiated. Foo a1; // Compiler error. template struct X {}; Foo a2; // OK since X is now defined.
用它来声明另一个类模板的成员:
templateclass Foo { X m; // OK as long as X is defined before // Foo is instantiated. }; Foo a1; // Compiler error. template struct X {}; Foo a2; // OK since X is now defined.
使用此类型定义函数模板或方法
templatevoid f1(X x) {} // OK if X is defined before calling f1 template X f2(){return X (); } // OK if X is defined before calling f2 void test1() { f1(X ()); // Compiler error f2 (); // Compiler error } template struct X {}; void test2() { f1(X ()); // OK since X is defined now f2 (); // OK since X is defined now }
你不能用不完整的类型做什么:
使用其中一个实例作为基类
class Foo : X{} // compiler error!
使用其中一个实例来声明成员:
class Foo { Xm; // compiler error! };
使用其中一个实例来定义函数或方法
void f1(Xx) {} // compiler error! X f2() {return X (); } // compiler error!
使用其中一个实例化的方法或字段,实际上是尝试取消引用具有不完整类型的变量
class Foo { X* m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };
创建类模板的显式实例化
template struct X;
在只使用指针或引用类的文件中.如果指针/引用,则不应调用任何成员/成员函数.
与class Foo;
//前向声明
我们可以声明Foo*或Foo&类型的数据成员.
我们可以使用Foo类型的参数和/或返回值声明(但不定义)函数.
我们可以声明Foo类型的静态数据成员.这是因为静态数据成员是在类定义之外定义的.