【问题标题】:Internal mechanism of virtual inheritance虚拟继承的内部机制
【发布时间】:2017-08-10 11:45:21
【问题描述】:

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){};
};

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

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

  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 的重复副本。

请帮我了解一下内部机制。

【问题讨论】:

  • @Ron 请在标记之前阅读实际帖子
  • @paper.plane 这个问题对于 SO 来说可能有点理论性......另外,虚拟继承的实际实现取决于编译器......看看标准实际上说什么看看:@987654322 @ 在第 231 页
  • @Ron 再说一遍,我想你可能还没有读过这个问题,只是用谷歌搜索了标题...... op 似乎不想知道 virtual 做什么(或 virtual 基类做什么)而是如何以及为什么生成所述类和继承类的内部对象模型
  • “内部机制”依赖于编译器,因此为了让 Stack Overflow 标准能够回答这个问题,您需要通过指定特定的工具链来缩小范围。
  • 一种选择是让编译器向B(int)C(int) 传递一个秘密参数,告诉他们是否应该调用A(int)。语言标准只是告诉我们效果,而不是如何实现。

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


【解决方案1】:

一个常见的用法(没有任何标准规定!)是首先创建一个虚拟继承对象的实例,然后在 vtables 中放置一个指向该实例的指针。这就是发生的事情:

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

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

【讨论】:

  • 它是否为虚拟继承创建了一个 v-table?正如我的问题中提到的,我同意它将添加一个虚拟基指针。但它和虚拟指针有什么不同吗?因为在上面的例子中,B 类的大小是 N(指针的大小)。但是如果在 B 类中添加一个虚函数,那么大小将变为 2N。(我已经检查过)因为编译器会另外添加 v-pointer 和 v-table。我的问题是仅针对虚拟继承,编译器会添加 v-table 吗?
  • @paper.plane:可能。正如我所说,这只是一个可能的实现。如果您想知道具体的实现,您应该准确说出您的目标编译器(以及版本和构建选项)。
  • vtable 是静态的。它永远不会改变,并且不能包含指向动态对象的指针!
猜你喜欢
  • 2016-10-26
  • 2013-08-24
  • 2015-09-01
  • 1970-01-01
  • 2016-03-26
  • 2011-01-08
相关资源
最近更新 更多