2008-12-23 95 views
13

我试图创建适当的头文件,其中不包含太多的其他文件以保持它们的干净并加快编译时间。转发基类的声明

我遇到的,而这样做的两个问题:

  1. 在基类向前声明不起作用。

    class B; 
    
    class A : public B 
    { 
    
        // ... 
    } 
    
  2. 对STD类的前向声明不起作用。

    namespace std 
    { 
        class string; 
    } 
    
    class A 
    { 
        string aStringToTest; 
    } 
    

如何解决这些问题?

回答

24

您无法解决的第一个问题。

第二个问题是没有什么关系的标准库类。这是因为你将类的实例声明为你自己类的成员。

这两个问题都是由于编译器必须能够从其定义中找出类的总大小。

但是,编译器可以计算出指向某个类的指针的大小,即使它尚未具有完整的定义。所以在这种情况下可能的解决方案是在消费类中有一个指针(或引用)成员。在基类案件

没有太大的帮助,因为你不会得到一个“是”的关系。

也不是值得做这样的事情std::string。首先,它应该是一个字符缓冲区的方便包装,可以让你从简单的事情上进行内存管理。如果你持有一个指向它的指针,为了避免包含头部,你可能会考虑一个好主意。其次(正如在评论中指出的那样),std::stringstd::basic_string<char>的类型定义。所以你需要转发declare(然后使用),相反,到那时候事情变得非常模糊和难以阅读,这是另一种成本。是不是真的值得吗?

+3

事实上,即使他使用了字符串指针或引用,TomWij也会遇到问题。他犯了错误,认为std :: string是一个类,事实并非如此。它是模板basic_string <>的typedef,并且根本无法进行前向声明。 – 2008-12-23 21:26:46

+4

@Shmoopty:准确地说,它不能被转发声明,因为模板参数的数量在标准中是未定义的。实现可以将其他模板参数添加到语言所需的参数。 – 2008-12-23 21:38:05

2

在这两种情况下,编译器需要知道类型的大小。因此,前向声明是不够的。基类可以添加成员或需要虚拟表。字符串成员将需要增加类的大小来存储STL字符串类的大小。

正向声明STL类往往是不可取的,因为实现通常包括加快编制明确模板实例。

3

你太艰苦设法解决的东西,实际上不是一个问题。使用你需要的头文件,并减少 - 在哪里可能 - 对它们的要求。但是,不要试图把它带到极端,因为你会失败。

在某些情况下,PIMPL成语可能对您有所帮助,但不在此处。

1

对于你的基类,你需要有完整的类型定义,而不仅仅是一个声明。派生类型头文件需要#include它们的基类的头文件。

对于std名称空间中的类,你必须包括正确的头文件 - <字符串>在这种情况下 - 然后做3两件事:

  1. 完全限定的类型:的std :: string aStringToTest

  2. 将一个使用声明仅用于 类型:using std :: string;

  3. 为 std命名空间使用声明:using namespace std;

7

正如埃里克回答过,你不能在任何的情况下,因为编译器需要知道类的大小使用前声明。

只能在一组操作使用前向声明:

  • 宣布采取声明的正向类作为参数或者返回它
  • 声明成员指针或引用向前声明的类
  • 功能
  • 宣布前声明的类型的静态变量在类定义

你不能用它来

  • 声明给定类型的一个成员属性(编译器要求的尺寸)
  • 定义或创建的类型的对象或将其删除
  • 呼叫之类的任何静态或构件的方法或访问任何构件或静态属性

(我也不曾忘记任何?)

考虑到,如果宣告auto_ptr是不一样的声明原始指针,因为auto_ptr研究所当超出范围并且删除需要完整声明类型时,antiation会尝试删除指针。如果您使用auto_ptr来保存前向声明类型,则必须提供析构函数(即使为空),并在完整的类声明被发现后对其进行定义。

还有一些其他的微妙之处。当你转发声明一个类时,你告诉编译器它将是一个类。这意味着它不能成为另一种类型的enumtypedef。这就是你所得到的问题,当你尝试转发声明std::string,因为它是一个模板的特定实例的类型定义:

typedef basic_string<char> string; // aproximate 

要转发声明字符串,你就需要转发声明basic_string模板,然后创建typedef。问题在于标准没有说明模板需要的参数的数量,它只是表明如果它需要多个参数,其余的参数必须具有默认类型,以便上面的表达式编译。这意味着没有标准的方式来转发宣告模板。

如果你要转发申报非标准模板另一方面(非STL,这是)你可以,只要你知道的参数个数做到这一点:

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 { 
    // ... 
}; 

最后,罗迪给你的建议是:尽可能地向前宣布,但假设必须包括一些事情。

2

>似乎前向声明对于基类和stl类是没用的。

更正... 对于基类和对象成员,前向声明是INAPPROPRIATE。 (它不是“无用的”,它是“不适用的”。)

当声明基类为另一个类的基类时,必须声明基类(未向前声明)。

当由另一个类,参数或返回值声明对象成员时,必须声明(未向前声明)对象成员。注意:by-reference或by-pointer没有这个约束。

更正... STL类的前向声明 - 根据ISO 14882 - 未定义的行为。 http://www.gotw.ca/gotw/034.htm