虚拟继承的内部机制

时间:2017-08-10 11:45:22

标签: c++ constructor virtual-inheritance language-implementation ctor-initializer

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. 对于每个构造函数,编译器都会为程序员提供每个定义的两个版本。从this link了解 例如在上面的代码中。 编译器将生成2个版本的C

    构造函数
     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的副本。

  3. 请帮助我理解内部机制。

1 个答案:

答案 0 :(得分:0)

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

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

但这只不过是一个可能是一个实现的理论答案。我不是说实际的实现(比如gcc,clang或microsoft vc)就是这样的。但是你可以使用它,例如,如果你必须用普通的C语言模仿虚拟继承。