C++ 非虚拟派生:我从编译器中真正得到了什么?

C++ 非虚拟派生:我从编译器中真正得到了什么?,c++,templates,derived-class,non-virtual-interface,C++,Templates,Derived Class,Non Virtual Interface,我想知道使用非虚拟派生时编译器会产生什么: template< unsigned int D > class Point { int[D]; // No virtual function // ... }; class Point2 : public Point<2> {}; class Point3 : public Point<3> {}; 唉,这会阻止客户端使用“简单”的转发声明: // No #include <Poi

我想知道使用非虚拟派生时编译器会产生什么:

template< unsigned int D >
class Point
{
     int[D];
    // No virtual function
    // ...
};
class Point2 : public Point<2> {};
class Point3 : public Point<3> {};
唉,这会阻止客户端使用“简单”的转发声明:

// No #include <Point.h>
class Point2;    // 'Point2': redefinition; different basic types
class Point3;    // 'Point3': redefinition; different basic types
//否#包括
类点2;//'第2点:重新定义;不同的基本类型
类第3点;//'第三点:重新定义;不同的基本类型
然后,必须编写这段相当不直观的代码:

// No #include <Point.h>
template< unsigned int > class Point;
typedef Point<2> Point2;
typedef Point<3> Point3;
//否#包括
模板类点;
类型定义点2;
类型定义点3;
这就是我放弃typedef并使用非虚拟派生的原因。不过,我想知道这一切意味着什么


(另一种策略是在专用头文件中编写一次转发声明,即la
#include

我真的不明白这里使用typedef有什么问题。这就是typedef的初衷。我相信在名称空间中工作时会有一些限制,但这里看起来并不是这样。这样的情况很常见:

常规.h

#include <map>
#include <string>

class MyObj1;
class MyObj2;

typedef map< string, MyObj1 > MyObj1Map;
typedef map< string, MyObj2 > MyObj2Map;
#包括
#包括
MyObj1类;
MyObj2类;
typedef mapMyObj1Map;
typedef mapMyObj2Map;

然后,您可以将其包含在源代码中,尽管您必须记住包含
MyObj1
MyObj2
的定义,其中编译器需要知道大小(即声明中的引用和指针除外)。

我不太明白这里使用typedef有什么问题。这就是typedef的初衷。我相信在名称空间中工作时会有一些限制,但这里看起来并不是这样。这样的情况很常见:

常规.h

#include <map>
#include <string>

class MyObj1;
class MyObj2;

typedef map< string, MyObj1 > MyObj1Map;
typedef map< string, MyObj2 > MyObj2Map;
#包括
#包括
MyObj1类;
MyObj2类;
typedef mapMyObj1Map;
typedef mapMyObj2Map;

然后,您可以将其包含在源代码中,尽管您必须记住包含
MyObj1
MyObj2
的定义,其中编译器需要知道大小(即声明中的引用和指针除外)。

我始终使用前向声明(typedef声明),由于以下原因,

  • 继承是继承的另一种选择

  • 由于以下原因,我总是使用远期申报单(typedef-one)

  • 继承是继承的另一种选择

  • 继承的问题是必须重新定义构造函数

    标准解决方案(我不明白你为什么不想要它)是一个专用的标题:

    // File PointFwd.h
    #ifndef POINT_FWD_H
    #define POINT_FWD_H 1
    
    template <unsigned> class Point;
    
    typedef Point<2> Point2;
    typedef Point<3> Point3;
    
    #endif
    
    //文件PointFwd.h
    #ifndef点_FWD_H
    #定义点_FWD_H 1
    模板类点;
    类型定义点2;
    类型定义点3;
    #恩迪夫
    

    您的客户端只需包含
    “PointFwd.h”
    即可向前声明所需内容。

    继承的问题是您必须重新定义构造函数

    标准解决方案(我不明白你为什么不想要它)是一个专用的标题:

    // File PointFwd.h
    #ifndef POINT_FWD_H
    #define POINT_FWD_H 1
    
    template <unsigned> class Point;
    
    typedef Point<2> Point2;
    typedef Point<3> Point3;
    
    #endif
    
    //文件PointFwd.h
    #ifndef点_FWD_H
    #定义点_FWD_H 1
    模板类点;
    类型定义点2;
    类型定义点3;
    #恩迪夫
    

    您的客户只需包含
    “PointFwd.h”
    即可向前声明他们想要的内容。

    好的,似乎到目前为止还没有人给您问题的实际答案:

    不,非虚拟派生没有开销。 编译器不必创建vtable,没有虚拟函数调用,一切正常。 它通常只需将基类的实例放在派生类的开头即可实现,因此指向派生类的指针也可以视为指向基类的指针。然后一切都正常了

    当然,构造函数调用必须被转发,但它们通常是内联的,这也消除了开销


    但是,如果使用多个基类,则可能会引入少量开销(取决于编译器如何实现它)。可能不太多(必须不时调整
    这个
    指针),但从理论上讲,它是存在的。

    好的,似乎到目前为止还没有人给您的问题提供实际答案:

    不,非虚拟派生没有开销。 编译器不必创建vtable,没有虚拟函数调用,一切正常。 它通常只需将基类的实例放在派生类的开头即可实现,因此指向派生类的指针也可以视为指向基类的指针。然后一切都正常了

    当然,构造函数调用必须被转发,但它们通常是内联的,这也消除了开销


    但是,如果使用多个基类,则可能会引入少量开销(取决于编译器如何实现它)。可能不会太多(必须不时调整
    这个
    指针),但从理论上讲,它确实存在。

    对不起,typedefs有什么问题?我无法向前声明类模板的typedef。例如,我不能转发declare
    类ostream
    ,我必须
    #include
    ——从那篇文章中可以看出,您必须使用
    的原因是正确的转发声明必须在std名称空间中完成,这对您来说是不合法的。我不明白为什么不能转发declare Point并将这些typedef放在专用的头文件中。@UncleBens:我引用GOTW的这篇文章的目的是举例说明专用转发声明头文件的使用。我可以使用这种技术,但我正在寻找另一种解决方案。对不起,typedefs有什么问题吗?我无法向前声明类模板的typedef。例如,我不能向前声明
    类ostream