Forward Declaration of a Base Class
我正在尝试创建适当的头文件,其中不包含太多其他文件,以使其保持干净并加快编译时间。
在执行此操作时,我遇到了两个问题:
基类的前向声明不起作用。
1 2 3 4 5 6 7 | class B; class A : public B { // ... } |
STD类的前向声明不起作用。
1 2 3 4 5 6 7 8 9 | namespace std { class string; } class A { string aStringToTest; } |
我该如何解决这些问题?
您无法解决的第一个问题。
第二个问题与标准库类无关。这是因为您将类的实例声明为您自己的类的成员。
这两个问题都是由于要求编译器必须能够从其定义中找出类的总大小而引起的。
但是,即使编译器还没有完整的定义,它也可以计算出指向该类的指针的大小。因此,在这种情况下,可能的解决方案是在消费类中具有一个指针(或引用)成员。
在基类情况下,没有太多帮助,因为您不会得到"是"关系。
对于
其次(如注释中所指出),
正如Earwicker之前回答的那样,在任何情况下都不能使用前向声明,因为编译器需要知道类的大小。
您只能在一组操作中使用前向声明:
- 声明将向前声明的类作为参数或返回它的函数
- 声明成员指针或对正向声明的类的引用
- 在类定义中声明前向声明类型的静态变量
您不能使用它来
- 声明给定类型的成员属性(编译器需要大小)
- 定义或创建该类型的对象或将其删除
- 调用类的任何静态或成员方法或访问任何成员或静态属性
(我忘了吗?)
请注意,声明
还有其他一些细微之处。当您向前声明一个类时,您在告诉编译器它将是一个类。这意味着它不能是另一种类型的
1 | typedef basic_string<char> string; // aproximate |
要转发声明字符串,您需要转发声明
另一方面,如果您要转发声明一个非标准模板(即非STL),则只要知道参数数量就可以这样做:
1 2 3 4 5 6 | template <typename T, typename U> class Test; // correct //template <typename T> class Test; // incorrect even if U has a default type template <typename T, typename U = int> class Test { // ... }; |
最后,罗迪(Roddy)向您提供的建议:向前声明尽可能多的声明,但是假设必须包括一些内容。
您正在努力解决实际上不是问题的事情。使用所需的头文件,并在可能的地方减少它们的需求。但不要尝试将其发挥到极致,因为那样会失败。
在某些情况下,PIMPL惯用语可能会为您提供帮助,但在这里没有帮助。
在这两种情况下,编译器都需要知道类型的大小。因此,前瞻性声明是不够的。基类可以添加成员或需要虚拟表。字符串成员将需要增加类的大小,以存储STL字符串类的大小。
通常不建议进行STL类的前向声明,因为实现通常包括显式的模板实例化,以加快编译速度。
>似乎前向声明对基类和stl类没有用。
更正...
对于基类和对象成员,前向声明是INAPPROPRIATE。 (这不是"无用的",而是"不适用的"。)
当一个基类被声明为另一个类的基类时,必须声明(不向前声明)。
当一个对象成员被另一个类声明,作为参数或作为返回值时,必须声明(不向前声明)。注意:按引用或按指针没有此约束。
更正...
根据ISO 14882,STL类的前向声明是未定义的行为。
http://www.gotw.ca/gotw/034.htm
对于基类,您需要具有完整的类型定义,而不仅仅是声明。派生类型标头将需要#include其基类的标头。
对于std名称空间中的类,您必须包括适当的标头(在这种情况下为
完全限定类型:std :: string
aStringToTest
放置一个use声明
该类型:使用std :: string;
放入using声明
std名称空间:使用名称空间std;