多重虚拟继承是否涉及虚拟函数的后期绑定式继承

Does multiple virtual inheritance involve late binding like inheritance of virtual functions?

本文关键字:虚拟 继承 绑定 是否 函数      更新时间:2023-10-16

与继承虚拟函数不同,解决虚拟继承似乎很简单,但也许我不够有创造力(狡猾?(。

虚拟继承是否与虚拟函数的继承有关?具体地说,虚拟继承会产生后期绑定吗?我看不出有什么原因。我只是因为关键词过载而怀疑。

我意识到该标准没有指定虚拟继承的实现。我对大多数非假设机器所做的一切都感兴趣,无论它们多么不完美。

正如虚拟函数涉及这些成员函数的后期绑定一样,我想您可以说虚拟继承涉及继承的数据成员的后期绑定。每个子类的内存布局可能非常不同,因此如果没有运行时类型信息,就无法解析像baseClassInstance->dataMember这样的表达式。因此,virtual的两种使用都需要使用"vtables"进行类特定查找。

请参阅Edsko de Vries的"多重和虚拟继承的内存布局",了解GNU编译器集群(gcc(如何实现虚拟继承,包括对象布局、结果等。据我所知,其他编译器在关键点上也类似。

虚拟继承并非没有运行时成本,但这种成本的原因不是灵活性的增加,而是模糊性的解决。

以多继承层次结构为例,其中类C通过不同的基类继承类A两次。对类型为C的对象的非静态方法A::foo的调用现在是不明确的(无论该调用是否是虚拟的(。问题是传递给成员函数的隐式this指针。通常,每个子类在内存中的位置都是由继承层次结构唯一确定的,但在这种情况下,由于AC中包含了两次,编译器必须决定如何调整成员函数调用的this指针——这是它自己无法做到的,所以它会要求您做出决定。

这个决定更加复杂,因为我们不仅可以调用A::fooC,还可以通过C的基类。这就造成了一个困境:根据我们用来进行调用的基类,编译器会对this指针进行不同的调整,将我们重定向到内存中A的不同位置,这取决于我们用于调用的指针类型。事实上,对于C的每一个实例,我们在内存中都有两个不同的A实例。

class A {
public:
    void foo();
    [...]
};
class B1 : public A {};
class B2 : public A {};
class C : public B1, public B2 {};
C c;
B1* b1 = &c;
B2* b2 = &c;
//assume foo() changes some internal state of A
b1->foo();
//the state change of the previous line is not visible
//to the next call - they operate on distinct instances of A
b2->foo();

virtual继承引入了一个额外的间接层来解决这种歧义。不是在编译时确定A相对于其子类的位置,而是执行运行时查找。这允许编译器传递相同的内存位置调用A::foo,无论调用是通过哪个派生类进行的。对于C的每个实例,我们现在在内存中只有一个A的实例。

class B1 : virtual public A {};
class B2 : virtual public A {};
[...]
C c;
B1* b1 = &c;
B2* b2 = &c;
//both calls will now operate on the same instance of A
//state changes performed by the one will be observed by the other
b1->foo();
b2->foo();