【问题标题】:How is C++'s multiple inheritance implemented?C++的多重继承是如何实现的?
【发布时间】:2009-06-16 16:11:37
【问题描述】:

单继承很容易实现。例如,在 C 中,继承可以模拟为:

struct Base { int a; }
struct Descendant { Base parent; int b; }

但是对于多重继承,编译器必须在新构建的类中安排多个父级。是怎么做到的?

我看到的问题是:父母应该安排在AB还是BA,或者甚至其他方式?然后,如果我进行演员表:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;

编译器必须考虑是否改变原始指针。虚拟机也需要类似的棘手操作。

【问题讨论】:

  • 你C模拟忘记了VTable(实现细节)指针。
  • @Dario:本文讨论的是多重继承中的重载问题,但不包含任何关于 C++ 中对象布局和对象转换的内容。
  • @Martin York:如果类中没有虚方法,则没有 v-table 指针。

标签: c++ inheritance compiler-construction multiple-inheritance language-implementation


【解决方案1】:

以下来自 C++ 创建者的论文描述了多重继承的可能实现:

Multiple Inheritance for C++ - Bjarne Stroustrup

【讨论】:

【解决方案2】:

this pretty old MSDN article 介绍了它是如何在 VC++ 中实现的。

【讨论】:

    【解决方案3】:

    然后,如果我进行演员表:

    SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;
    

    编译器必须考虑是否改变原始指针。虚拟机也有类似的棘手问题。

    使用非虚拟继承,这比你想象的要简单——在编译转换时,编译器知道派生类的确切布局(毕竟,编译器做了布局)。通常发生的所有事情都是从派生类指针中添加/减去固定偏移量(对于其中一个基类可能为零)。

    使用虚拟继承可能会更复杂一些 - 它可能涉及从 vtbl(或类似的)获取偏移量。

    Stan Lippman 的书 "Inside the C++ Object Model" 很好地描述了这些东西可能(并且通常实际上是)如何工作。

    【讨论】:

      【解决方案4】:

      父母按照指定的顺序排列:

      class Derived : A, B {} // A comes first, then B
      
      class Derived : B, A {} // B comes first, then A
      

      您的第二种情况是以编译器特定的方式处理的。一种常见的方法是使用大于平台指针大小的指针来存储额外数据。

      【讨论】:

      • 这可能很常见,但我认为不是必需的。
      • 我已经看到了另一种方式。这一切都取决于实施。
      • 顺序只对构造函数调用的顺序有影响。但布局未指定。前阵子做了测试,GCC把空基类放在内存中,利用空基类优化。
      【解决方案5】:

      这是一个有趣的问题,实际上并不是 C++ 特定的。当您拥有一种具有多重分派和多重继承的语言(例如 CLOS)时,事情也会变得更加复杂。

      人们已经注意到有不同的方法来解决这个问题。在这种情况下,您可能会发现阅读一些有关元对象协议 (MOP) 的有趣内容...

      【讨论】:

      • 我认为如果该语言不支持指向引用对象的“原始”指针并且不支持 POD 向后兼容性,那么实现起来会容易得多。因为如果他们进行对象指针的转换是非常棘手的。一种语言可以根据需要将尽可能多的元信息添加到引用类和类实例中。在 C++ 中,这不是很容易做到的。
      【解决方案6】:

      这完全取决于编译器是如何完成的,但我相信它通常是通过 vtables 的层次结构完成的。

      【讨论】:

        【解决方案7】:

        我做了一个简单的实验:

        class BaseA { int a; };
        class BaseB { int b; };
        class Descendant : public BaseA, BaseB {};
        int main() {
                Descendant d;
                BaseB * b = (BaseB*) &d;
                Descendant *d2 = (Descendant *) b;
                printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2);
        }
        

        输出是:

        Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0
        

        很高兴认识到静态转换并不总是意味着“在不触及内容的情况下更改类型”。 (嗯,当数据类型不匹配时,也会对内容产生干扰,但 IMO 情况不同)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-26
          • 1970-01-01
          • 2011-11-21
          • 2020-07-01
          • 2011-10-17
          相关资源
          最近更新 更多