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

发布于 2024-09-24 04:49:40 字数 1154 浏览 16 评论 0原文

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

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

这里的派生仅意味着编译时检查吗?或者还有其他开销吗?

我注意到我的编译器在使用 Point2 或直接使用 Point<2> 时会生成相同大小的对象。我推断推导不会产生虚函数表,因此,不会进行任何虚拟调用。

我错过了什么吗?


上下文

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

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 并使用非虚拟派生的原因。尽管如此,我还是想知道其中的含义是什么。

(另一种策略是在专用头文件中编写一次前向声明,就像 #include 一样。)

I am wondering what is produced by the compiler when using non-virtual derivation:

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

Does the derivation here only imply compile-time checks? Or is there some other overhead?

I noticed my compiler produces equally sized objects when using Point2 or directly Point<2>. I deduce the derivation did not incur a vtable, and, as a consequence, no virtual call will ever be made.

Am I missing something?


Context

I want to provide a couple of predefined specializations of a given class template. I started with typedefs:

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

Alas, this prevents clients to use "simple" forward declarations:

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

It is then mandatory to write this rather unintuitive piece of code:

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

This is why I discarded typedefs and used non-virtual derivation. Still, I am wondering what are all the implications.

(Another strategy would be to write the forward declaration once in a dedicated header file, à la #include <iosfwd>.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

离鸿 2024-10-01 04:49:40

好吧,看起来到目前为止没有人给你一个实际的答案:

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

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

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

Ok, looks like no one have so far given you an actual answer to your question:

No, there is no overhead to non-virtual derivation.
The compiler doesn't have to create a vtable, there are no virtual function calls, and all is well.
It is typically implemented simply by placing an instance of the base class at the beginning of the derived class, so that a pointer to the derived class can be treated as a pointer to the base class as well. And then everything just works.

Of course, constructor calls have to be forwarded, but they will usually get inlined, eliminating that overhead as well.

However, if you use multiple base classes, it may introduce a tiny bit of overhead (depending on how the compiler implements it). Probably not much (the this pointer has to be adjusted from time to time), but theoretically, it is there.

凉栀 2024-10-01 04:49:40

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

General.h

#include <map>
#include <string>

class MyObj1;
class MyObj2;

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

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

I don't really see what the problem is with using typedefs here. This is the sort of thing that typedef was intended for. I believe that there are a few restrictions when working in namespaces, but it doesn't look like you're doing that here. It's quite common to see something like this:

General.h

#include <map>
#include <string>

class MyObj1;
class MyObj2;

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

You can then include that in your source code, though you'll have to remember to include the definition of MyObj1 and MyObj2 where the compiler will need to know the size (i.e other than references and pointers in declarations.)

城歌 2024-10-01 04:49:40

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

  1. 继承是泛型的替代方案。

I always go with the forward declaration (the typedef one), because of the following reasons;

  1. Inheritance is an alternative to generics.
梦一生花开无言 2024-10-01 04:49:40

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

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

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

The problem with inheritance is that you have to redefine the constructors.

The standard solution (I don't see why you don't want it) is a dedicated header:

// 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

Your clients only have to include "PointFwd.h" to forward declare what they want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文