1

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

template< unsigned int D >
class Point
{
     int[D];
    // No virtual function
    // ...
};
class Point2 : public Point<2> {};
class Point3 : public Point<3> {};

这里的推导是否只意味着编译时检查?还是有其他开销?

我注意到我的编译器在直接使用Point2or时会产生同样大小的对象Point<2>。我推断推导不会产生 vtable,因此,永远不会进行虚拟调用。

我错过了什么吗?


语境

我想为给定的类模板提供几个预定义的特化。我从 typedefs 开始:

template< unsigned int D >
class Point
{
     int[D];
};
typedef Point<2> Point2;
typedef Point<3> Point3;

唉,这会阻止客户端使用“简单”的前向声明:

// No #include <Point.h>
class Point2;    // 'Point2': redefinition; different basic types
class Point3;    // 'Point3': redefinition; different basic types

然后必须编写这段相当不直观的代码:

// No #include <Point.h>
template< unsigned int > class Point;
typedef Point<2> Point2;
typedef Point<3> Point3;

这就是我放弃 typedef 并使用非虚拟派生的原因。不过,我想知道所有的含义是什么。

(另一种策略是在专用头文件中编写一次前向声明,à la #include <iosfwd>。)

4

4 回答 4

1

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

不,非虚拟派生没有开销。编译器不必创建 vtable,没有虚函数调用,一切都很好。它通常简单地通过将基类的实例放在派生类的开头来实现,这样指向派生类的指针也可以被视为指向基类的指针。然后一切正常

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

但是,如果您使用多个基类,它可能会引入一点点开销(取决于编译器如何实现它)。可能不多(this指针要不时调整),但理论上是有的。

于 2010-09-28T09:34:42.847 回答
0

我真的不明白在这里使用 typedef 有什么问题。这就是 typedef 的用途。我相信在命名空间中工作时有一些限制,但看起来你在这里并没有这样做。看到这样的事情很常见:

一般.h

#include <map>
#include <string>

class MyObj1;
class MyObj2;

typedef map< string, MyObj1 > MyObj1Map;
typedef map< string, MyObj2 > MyObj2Map;

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

于 2010-09-21T16:18:35.177 回答
0

由于以下原因,我总是使用前向声明(typedef 之一);

  1. 继承是泛型的替代方案。
于 2010-09-21T18:33:08.543 回答
0

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

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

// 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"转发声明他们想要的内容。

于 2010-09-28T08:58:41.873 回答