基类中的虚拟继承和空 vtable
有这样的代码:
#include <iostream>
class Base
{
int x;
};
class Derived : virtual public Base
{
int y;
};
int main()
{
std::cout << sizeof(Derived) << std::endl; // prints 12
return 0;
}
我读到,当某个类被虚拟继承时,就会为派生类创建空的 vtable,所以内存布局如下:
Derived::ptr to empty vtable
Derived::y
Base::x
它是 12 个字节。问题是 - 如果没有任何虚拟方法,这个空 vtable 的目的是什么?它是如何使用的?
There is this code:
#include <iostream>
class Base
{
int x;
};
class Derived : virtual public Base
{
int y;
};
int main()
{
std::cout << sizeof(Derived) << std::endl; // prints 12
return 0;
}
I have read that when some class is virtually inherited then there is created empty vtable for class Derived, so memory layout is as follows:
Derived::ptr to empty vtable
Derived::y
Base::x
and it is 12 bytes. The question is - what is purpose of this empty vtable if there are not any virtual methods and how is it used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Derived
需要某种方式来知道Base
子对象在哪里。对于虚拟继承,基类的相对位置相对于派生类的位置并不固定:它可以位于完整对象中的任何位置。考虑一个涉及钻石继承的更典型的例子。
在这里,
B1
和B2
实际上都是从A
派生的,因此在C
中,只有一个一个子对象。
B1
和B2
都需要知道如何找到A
子对象(以便它们可以访问a
成员变量,或者A
的其他成员(如果我们要定义它们)。这就是 vtable 在本例中的用途:
B1
和B2
都会有一个 vtable,其中包含A
子对象的偏移量。为了演示编译器可以执行哪些操作来实现上述菱形继承示例,请考虑由 Visual C++ 11 开发人员预览版生成的以下类布局和虚拟表。
以及以下 vtable:
请注意,偏移量是相对于 vtable 的地址而言的,并且请注意,对于为
的
,偏移量不同。B1
和B2
子对象生成的两个 vtable C(另请注意,这完全是一个实现细节 - 其他编译器可能以不同的方式实现虚函数和基类。此示例演示了它们的一种实现方式,而且它们通常以这种方式实现。)
Derived
needs some way to know where theBase
subobject is. With virtual inheritance, the relative location of the base class is not fixed with respect to the location of the derived class: it may be located anywhere in the full object.Consider a more typical example involving diamond inheritance.
Here, both
B1
andB2
derive virtually fromA
, so inC
, there is exactly oneA
subobject. BothB1
andB2
need to know how to find thatA
subobject (so that they can access thea
member variable, or other members ofA
if we were to define them).This is what the vtable is used for in this case: both
B1
andB2
will have a vtable that contains the offset of theA
subobject.To demonstrate what a compiler might do to implement the above diamond inheritance example, consider the following class layouts and virtual tables, generated by the Visual C++ 11 Developer Preview.
and the following vtables:
Note that the offsets are relative to the address of the vtable, and note that for the two vtables generated for the
B1
andB2
subobjects ofC
, the offsets are different.(Also note that this is entirely an implementation detail--other compilers may implement virtual functions and bases differently. This example demonstrates one way that they are implemented, and they are very commonly implemented this way.)
为了实现虚函数,C++ 使用一种特殊的后期绑定形式,称为虚拟表。虚拟表是用于以动态/后期绑定方式解析函数调用的函数查找表。虚拟表有时有其他名称,例如“vtable”、“虚拟函数表”、“虚拟方法表”或“调度表”。
虚拟表其实很简单。首先,每个使用虚函数的类(或者从使用虚函数的类派生)都被赋予它自己的虚表。该表只是编译器在编译时设置的一个静态数组。虚表包含每个可以由类的对象调用的虚函数的条目。该表中的每个条目只是一个函数指针,指向该类可访问的最派生函数。
使用虚拟函数)被赋予它自己的虚拟表作为
秘密数据成员。
虚函数可以被类的对象调用。
甚至为具有虚拟基类的类创建虚拟表。在这种情况下,vtable 具有指向基类的共享实例的指针以及指向该类的虚拟函数(如果有)的指针。
To implement virtual functions, C++ uses a special form of late binding known as the virtual table. The virtual table is a lookup table of functions used to resolve function calls in a dynamic/late binding manner. The virtual table sometimes goes by other names, such as “vtable”, “virtual function table”, “virtual method table”, or “dispatch table”.
The virtual table is actually quite simple. First, every class that uses virtual functions (or is derived from a class that uses virtual functions) is given it’s own virtual table. This table is simply a static array that the compiler sets up at compile time. A virtual table contains one entry for each virtual function that can be called by objects of the class. Each entry in this table is simply a function pointer that points to the most-derived function accessible by that class.
that uses virtual functions) is given it's own virtual table as a
secret data member.
virtual function that can be called by objects of the class.
Virtual Table is created even for classes that have virtual base classes. In this case, the vtable has pointer to the shared instance of the base class along with the pointers to the classe's virtual functions if any.
如果执行dynamic_cast(ptr_to_obj),它将使用vtable指针来确定ptr_to_obj是否引用Derived。每个涉及虚方法或继承的类都需要一个 vtable,并且每个类都需要有一个不同的 vtable 以支持dynamic_cast<>。即使它不包含任何指向方法的指针,它仍然用于标识对象的类型。
If you do dynamic_cast<Derived*>(ptr_to_obj), it will use the vtable pointer to determine whether ptr_to_obj refers to a Derived or not. Every class which is involved with virtual methods or inheritance needs a vtable, and it needs to be distinct for each class to support dynamic_cast<>. Even if it doesn't contain any pointers to methods, it is still used to identify the type of the object.