c++ - 多重虚拟继承中的虚拟表和内存布局

标签 c++ multiple-inheritance vtable virtual-inheritance memory-layout

考虑以下层次结构:

struct A {
   int a; 
   A() { f(0); }
   A(int i) { f(i); }
   virtual void f(int i) { cout << i; }
};
struct B1 : virtual A {
   int b1;
   B1(int i) : A(i) { f(i); }
   virtual void f(int i) { cout << i+10; }
};
struct B2 : virtual A {
   int b2;
   B2(int i) : A(i) { f(i); }
   virtual void f(int i) { cout << i+20; }
};
struct C : B1, virtual B2 {
   int c;
   C() : B1(6),B2(3),A(1){}
   virtual void f(int i) { cout << i+30; }
};
  1. C 实例的确切 内存布局是什么?它包含多少个 vptr,每个 vptr 的确切位置?哪些虚表与C的虚表共享?每个虚拟表到底包含什么?

    这里是我对布局的理解:

    ----------------------------------------------------------------
    |vptr1 | AptrOfB1 | b1 | B2ptr | c | vptr2 | AptrOfB2 | b2 | a |
    ----------------------------------------------------------------
    

    其中 AptrOfBx 是指向 Bx 包含的 A 实例的指针(因为继承是虚拟的)。
    那是对的吗? vptr1 指向哪些函数? vptr2 指向哪些函数?

  2. 给定以下代码

    C* c = new C();
    dynamic_cast<B1*>(c)->f(3);
    static_cast<B2*>(c)->f(3);
    reinterpret_cast<B2*>(c)->f(3);
    

    为什么所有对 f 的调用都打印 33

最佳答案

虚拟基地与普通基地有很大的不同。请记住,“虚拟”意味着“在运行时确定”——因此整个基本子对象 必须在运行时确定。

假设您正在获取一个 B & x 引用,并且您的任务是找到 A::a 成员。如果继承是真实的,那么 B 有一个父类(super class) A,因此您正在通过 x< 查看的 B 对象 有一个 A 子对象,您可以在其中找到您的成员 A::a。如果 x 的最派生对象有多个 A 类型的基类,那么您只能看到作为 B 子对象的特定拷贝.

但是如果继承是虚拟的,那么这一切都没有意义。我们不知道我们需要哪个 A-subobject——这个信息在编译时根本不存在。我们可以处理一个实际的 B 对象,如 B y; B & x = y;,或使用 C 对象,如 C z; B & x = z;,或者从 A 多次派生的完全不同的东西。唯一知道的方法是在运行时 A 找到实际的基数

这可以通过多一层运行时间接实现。 (请注意,与非虚拟函数相比,这与虚拟 函数 是如何通过额外一级的运行时间接实现的方式完全平行的。)一个解决方案不是使用指向 vtable 或基本子对象的指针,而是存储指向实际基子对象的指针。这有时被称为“thunk”或“trampoline”。

所以实际对象 C z; 可能如下所示。内存中的实际排序取决于编译器并且并不重要,我已经抑制了 vtables。

+-+------++-+------++-----++-----+
|T|  B1  ||T|  B2  ||  C  ||  A  |
+-+------++-+------++-----++-----+
 |         |                 |
 V         V                 ^
 |         |       +-Thunk-+ |
 +--->>----+-->>---|     ->>-+
                   +-------+

因此,无论您有 B1& 还是 B2&,您首先要查找 thunk,thunk 会告诉您在哪里可以找到实际的 base子对象。这也解释了为什么您不能执行从 A& 到任何派生类型的静态转换:此信息在编译时根本不存在。

要获得更深入的解释,请查看 this fine article . (在该描述中,thunk 是 C 的 vtable 的一部分,虚拟继承总是需要维护 vtables,即使任何地方都没有虚拟函数。)

关于c++ - 多重虚拟继承中的虚拟表和内存布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30891915/

相关文章:

c++ - Qt QTreeWidget 替代 IndexFromItem?

python - Python中的Lookup Chain和MRO有什么关系?

c++ - 将 double 插入到 stringstream 时避免舍入

c++ - 测量移植到嵌入式平台的 C++ 的静态内存使用情况

c++ - 什么时候虚拟继承是一个好的设计?

C++ 继承问题 : undefined reference to 'vtable'

C++ 类型对 `vtable for Producer' 的 undefined reference

c++ - 派生类调用基类对象的方法

c++ - 如何在 cocos2d-x 中使用 Sprite 表创建菜单

android - 继承ActionBarSherlock和Android YouTubePlayer