多继承中vtable c++中保存的重写虚方法在哪里

where is the overridden virtual method saved in the vtable c++ in multiple inheritance

在 C++ 中,运行 时没有 class 表示,但我总是可以在派生的 class 中调用重写的虚拟方法。 vtable 中保存的重写方法在哪里?这是一段代码来演示:

struct B1 {
 virtual void f() { ... }
};
struct B2 {
 virtual void f() { ... }
 virtual void g() { ... }
};
struct D : B1, B2 {
 void f() { ... }
 virtual void h() { ... }
};

classD 对象的内存布局是什么? B1::f 和 B2::f 保存在那个内存布局中的什么位置(如果它们被保存的话)?

Class D 的对象 d 将只有一个指向 class DVMT 的指针,它将包含指向 D::f 的指针。 由于 B1:f 和 B2::f 只能从 D class 的范围内静态调用,因此对象 d 不需要保留指向那些被覆盖方法的动态指针。

这就是标准中没有定义的原因,这只是编译器的usual/logical实现。

其实情况更复杂,因为class D的VMT合并了classes B1和B2的VMT。但是无论如何,在创建class B1的对象之前,不需要动态调用B1::f。

当编译器使用virtual dispatch*的vtable方法时,重写成员函数的地址存放在基类的vtable中class其中定义了函数。

每个 class 都可以访问其所有基数 class 的 vtable。这些 vtable 存储在 class 本身的内存布局之外。每个具有虚拟成员函数的 class,声明的或继承的,都有一个指向它自己的 vtable 的指针。当您调用覆盖的成员函数时,您需要提供要调用其成员函数的基 class 的名称。编译器知道所有 classes 的 vtable,它知道如何定位基 class 的 vtable,在编译时进行查找,并且直接调用成员函数

这是一个简短的例子:

struct A {
    virtual void foo()   { cout << "A"; }
};
struct B : public A { }; // No overrides
struct C : public B {
    virtual void foo()   { cout << "C"; }
    void bar()           { B::foo(); }
};

Demo.

在上面的示例中,编译器需要查找 B::foo,它未在 class B 中定义。编译器查询其符号 table 发现 B::foo 是在 A 中实现的,并在 C::bar.

中生成对 A::foo 的调用

* vtables 不是实现虚拟调度的唯一方法。 C++ 标准不要求使用 vtables。

虽然 C++ 标准没有强制要求,但每个已知的 C++ 实现都使用相同的方法:每个 class 至少有一个虚函数都有一个 vptr(指向 vtable 的指针)。

你没有提到虚拟继承,这是一种不同的、更微妙的继承关系;非虚拟继承是基础 class 子对象和派生 class 之间的简单排他关系。 我会假设在这个答案中所有的继承关系都不是虚拟的。

这里我假设我们派生自 classes 至少有一个虚函数。

在单继承的情况下,来自基础 class 的 vptr 被重用。 (不重复使用它只会浪费 space 和 运行 时间。)基础 class 称为“主要基础 class”。

在多重继承的情况下,派生class的布局包含每个基class的布局,就像C中结构的布局包含每个成员的布局一样。 D 的布局是 B1 然后 B2 (实际顺序是任意的,但通常保持源代码顺序)。

第一个 class 是主基 class:在 D 中,来自 B1 的 vptr 指向 D 的完整 vtable,vtable具有 D 的所有虚函数。来自非主库 class 的每个 vptr 指向 D 的辅助 vtable:一个仅包含来自该辅助库 class.

的虚函数的 vtable

D 的构造函数必须初始化 class 实例的每个 vptr 以指向 D.

的适当 vtable