虚拟继承的内部机制



C++示例代码:

class A {
public:
A(int) {}
};
class B : public virtual A {
public:
B(int b) : A(b) {}
};
class C : virtual public A {
public:
C(int c) : A(c) {}
};
class D : public B, public C {
public:
D() : B(1), C(2){};
};

这是钻石问题的典型代码(解决方案(。我知道为什么使用虚拟关键字。但是我不知道编译器处理该问题的内部机制。现在我遇到了关于上述机制的两种不同的理论,如下所述。

  1. 当使用 virtual 关键字继承类时,编译器会在派生类中添加虚拟基指针。我已经检查了派生类的大小,是的,它包括一个附加指针的大小。但我不知道它指向哪里,以及在上面的例子中在 D 类中引用类 A 的成员时它是如何工作的。

  2. 对于每个构造函数,编译器为程序员提供的每个定义创建两个版本。从这个链接知道 例如,在上面的代码中。 编译器将生成 C 构造函数的 2 个版本

    C(int){}           // Version1
    C(int):A(int){}    // Version2 
    

    和构造函数 B 的两个不同版本

    B(int){}           // Version1
    B(int):A(int){}    // Version2
    

    因此,当构造 D 时,编译器将生成以下任一代码

    D() : B(), C(2) {}  // Version1
    D() : B(1), C() {}  // Version2
    

    以确保只创建 A 的一个实例,从而避免 A 的重复副本。

请帮助我了解内部机制。

一种常见的用法(未由任何标准指定!(是首先创建虚拟继承对象的实例,并在 vtables 中放置一个指向该对象的指针。所以这是发生的事情:

  • 创建 A:没什么特别的
  • 创建 B:构造 A 并在 B vtable 中添加指向它的链接
  • 创建 D:首先构造 A,然后构造 B 和 C,并且每个都在其 vtable 中包含指向 A 的链接。这允许当你获得指向 D 对象的指针时,将其强制转换为指向 B 和 C 的指针,并且每个指针仍将知道其 A 成员的位置。

但这只不过是对什么是实现的理论答案。我并不是说实际的实现(比如gcc,clang或Microsoft vc(完全遵循这一点。但是,例如,如果您必须用纯 C 语言模仿虚拟继承,则可以使用它。

最新更新