基准的前瞻性声明

时间:2008-12-23 20:13:45

标签: c++ class forward-declaration

我正在尝试创建正确的头文件,其中不包含太多其他文件以保持其清洁并加快编译时间。

我这样做时遇到两个问题:

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

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

    namespace std
    {
        class string;
    }
    
    class A
    {
        string aStringToTest;
    }
    
  3. 如何解决这些问题?

6 个答案:

答案 0 :(得分:25)

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

第二个问题与标准库类无关。这是因为您将类的实例声明为您自己类的成员。

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

但是,编译器可以计算出指向类的指针的大小,即使它还没有完整的定义。因此,在这种情况下,可能的解决方案是在消费类中使用指针(或引用)成员。

在基类案例中帮助不大,因为你不会得到'是'关系。

std::string之类的东西也不值得做。首先,它应该是一个方便的字符缓冲区包装器,以避免对这么简单的事情进行内存管理。如果你拿着一个指向它的指针,只是为了避免包含标题,你可能已经过了一个好主意。

其次(正如评论中所指出的),std::stringstd::basic_string<char>的typedef。因此,您需要转发声明(然后使用),而这时候事情变得非常模糊和难以阅读,这是另一种成本。这真的值得吗?

答案 1 :(得分:7)

正如Earwicker之前所回答的,你不能在任何这些情况下使用前向声明,因为编译器需要知道类的大小。

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

  • 声明将前向声明的类作为参数或返回它的函数
  • 声明成员指针或对前向声明的类的引用
  • 在类定义
  • 中声明前向声明类型的静态变量

你不能用它来

  • 声明给定类型的成员属性(编译器需要大小)
  • 定义或创建该类型的对象或将其删除
  • 调用类的任何静态或成员方法或访问任何成员或静态属性

(我忘了吗?)

考虑到声明auto_ptr与声明原始指针不同,因为auto_ptr实例化将在超出范围时尝试删除指针并且删除需要完整声明这种类型。如果使用auto_ptr in来保存前向声明的类型,则必须提供析构函数(即使为空),并在看到完整的类声明后定义它。

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

typedef basic_string<char> string; // aproximate

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

另一方面,如果你想要转发声明一个非标准模板(非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 {
   // ...
};

最后,Roddy给你的建议:尽可能多地向前宣布,但假设必须包含一些东西。

答案 2 :(得分:2)

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

前向声明STL类通常是不可取的,因为实现通常包括加速编译的显式模板实例化。

答案 3 :(得分:2)

你正在努力解决一些实际上不是问题的事情。使用您需要的头文件,并减少 - 在可能的情况下 - 对它们的要求。但是不要试图把它带到极端,因为你会失败。

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

答案 4 :(得分:1)

对于基类,您需要具有完整的类型定义,而不仅仅是声明。派生类型标头需要#include其基类的标头。

对于std命名空间中的类,必须包含正确的标题 - &lt; string&gt;在这种情况下 - 然后做三件事之一:

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

  2. 将just声明用于just 那个类型:使用std :: string;

  3. 输入使用声明 std namespace:using namespace std;

答案 5 :(得分:1)

&GT;似乎前向声明对基类和stl类没用。

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

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

当被另一个类声明或作为参数声明或作为返回值时,必须声明一个对象成员(不是前向声明的)。注意:按引用或按指针没有该约束。

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