基类中的虚拟继承和空vtable
Virtual inheritance and empty vtable in base class
有这样的代码:
#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;
}
我读到,当某个类实际上是继承的时,就会为Derived类创建空的vtable,所以内存布局如下:
Derived::ptr to empty vtable
Derived::y
Base::x
并且它是12个字节。问题是,如果没有任何虚拟方法,这个空vtable的目的是什么?它是如何使用的?
Derived
需要某种方法来知道Base
子对象在哪里。使用虚拟继承,基类相对于派生类的位置不是固定的:它可能位于整个对象中的任何位置。
考虑一个涉及钻石继承的更典型的例子。
struct A
{
int a;
};
struct B1 : virtual A
{
int b1;
};
struct B2 : virtual A
{
int b2;
};
struct C : B1, B2
{
int c;
};
这里,B1
和B2
实际上都是从A
派生的,所以在C
中,正好有一个子对象A
。B1
和B2
都需要知道如何找到A
子对象(这样他们就可以访问a
成员变量,或者如果我们要定义A
的其他成员)。
这就是vtable在这种情况下的用途:B1
和B2
都将有一个包含A
子对象偏移量的vtable。
为了演示编译器可以如何实现上述菱形继承示例,请考虑以下由Visual C++11 Developer Preview生成的类布局和虚拟表。
class A size(4):
+---
0 | a
+---
class B1 size(12):
+---
0 | {vbptr}
4 | b1
+---
+--- (virtual base A)
8 | a
+---
class B2 size(12):
+---
0 | {vbptr}
4 | b2
+---
+--- (virtual base A)
8 | a
+---
class C size(24):
+---
| +--- (base class B1)
0 | | {vbptr}
4 | | b1
| +---
| +--- (base class B2)
8 | | {vbptr}
12 | | b2
| +---
16 | c
+---
+--- (virtual base A)
20 | a
+---
以及以下vtables:
B1::$vbtable@:
0 | 0
1 | 8 (B1d(B1+0)A)
B2::$vbtable@:
0 | 0
1 | 8 (B2d(B2+0)A)
C::$vbtable@B1@:
0 | 0
1 | 20 (Cd(B1+0)A)
C::$vbtable@B2@:
0 | 0
1 | 12 (Cd(B2+0)A)
注意,偏移量相对于vtable的地址,并且注意,对于为C
的B1
和B2
子对象生成的两个vtable,偏移量是不同的。
(还要注意,这完全是一个实现细节——其他编译器可能会以不同的方式实现虚拟函数和基。这个例子演示了它们的一种实现方式,而且它们通常是以这种方式实现的。)
为了实现虚拟函数,C++使用了一种特殊形式的后期绑定,称为虚拟表。虚拟表是用于以动态/后期绑定方式解析函数调用的函数的查找表。虚拟表有时使用其他名称,如"vtable"、"虚拟函数表"、"虚方法表"或"调度表"。
虚拟表实际上非常简单。首先,每个使用虚拟函数的类(或从使用虚拟函数类派生的类)都有自己的虚拟表。此表只是编译器在编译时设置的静态数组。虚拟表为类的对象可以调用的每个虚拟函数包含一个条目。该表中的每个条目都只是一个函数指针,指向该类可访问的最派生的函数。
- 使用虚拟函数(或从类派生)的每个类使用虚拟函数)的虚拟表秘密数据成员
- 此表由编译器在编译时设置
- 虚拟表包含一个条目作为每个条目的函数指针类的对象可以调用的虚拟函数
- 虚拟表存储指向纯虚拟函数的NULL指针
即使对于具有虚拟基类的类,也会创建虚拟表。在这种情况下,vtable具有指向基类的共享实例的指针,以及指向类的虚拟函数(如果有的话)的指针
如果执行dynamic_cast<派生*>(ptr_to_obj),它将使用vtable指针来确定ptr_to_obj是否引用了Derived。每个涉及虚拟方法或继承的类都需要一个vtable,并且每个类都需要不同的vtable才能支持dynamic_cast<>。即使它不包含任何指向方法的指针,它仍然用于标识对象的类型。
- 继承函数的重载解析
- 继承期间显示未知行为的子类
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- 通过继承类使用来自不同命名空间的运算符
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 混合组合和继承的C++问题
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 从类继承时,继承的类是否会通过父类重新定义继承的变量
- 虚拟继承情况下的 vtable
- 为什么虚拟继承即使不涉及虚拟功能也需要 vtable?
- VTable如何处理多个继承
- C vtable在多个继承中,指向thunk方法的指针
- C++ 继承问题:未定义对"vtable"的引用
- 对"vtable for "命名空间继承的未定义引用 对"类型信息"的未定义引用
- 对具有继承的 vtable 的未定义引用
- 使用CMake构建Qt项目并从QMainWindow继承会导致未引用的vtable错误
- C#与C++——类型、继承和vtable
- 基类中的虚拟继承和空vtable
- 在Qt中,我的类继承了带有"vtable for GameControlCenter"的QObject错误,引用自: