我刚刚在C++ FAQ Lite中阅读了有关此内容的信息

[25.10]通过虚拟继承“委托(delegate)给姐妹类”是什么意思?

 class Base {
 public:
   virtual void foo() = 0;
   virtual void bar() = 0;
 };

 class Der1 : public virtual Base {
 public:
   virtual void foo();
 };

 void Der1::foo()
 { bar(); }

 class Der2 : public virtual Base {
 public:
   virtual void bar();
 };

 class Join : public Der1, public Der2 {
 public:
   ...
 };

 int main()
 {
   Join* p1 = new Join();
   Der1* p2 = p1;
   Base* p3 = p1;

   p1->foo();
   p2->foo();
   p3->foo();
 }

“信不信由你,当Der1::foo()调用this-> bar()时,它最终会调用Der2::bar()。是的,这是正确的:Der1一无所知的类将提供a的覆盖。由Der1::foo()调用的虚拟函数。这种“交叉委托(delegate)”可以是一种强大的技术,用于自定义多态类的行为。

我的问题是:
  • 幕后发生了什么。
  • 如果添加Der3(虚拟继承自Base),会发生什么? (我这里没有编译器,目前无法测试。)
  • 最佳答案



    简单的解释是,由于BaseDer1中的Der2继承都是虚拟的,因此在派生程度最高的对象Join中只有一个对象实例。在编译时,并假设(这是常见情况)虚拟表作为调度机制,在编译Der1::foo时,它将通过vtable将调用重定向到bar()

    现在的问题是,编译器如何为每个对象生成vtable,Base的vtable将包含两个空指针,Der1的vtable将包含Der1::foo和空指针,Der2的vtable将包含空指针和Der2::bar [* ]

    现在,由于在上一级具有虚拟继承性,因此编译器在处理Join时将创建单个Base对象,从而为BaseJoin子对象创建单个vtable。它有效地合并了Der1Der2的vtable,并生成了一个包含指向Der1::fooDer2::bar的指针的vtable。

    因此,Der1::foo中的代码将通过Join的vtable调度到最终的替代程序,在这种情况下,替代程序在虚拟继承层次结构的另一个分支中。

    如果添加Der3类,并且该类定义了两个虚函数,则编译器将无法干净地合并这三个vtable并会提示,并且会出现一些与乘法定义方法的歧义有关的错误(没有覆盖程序)可以被认为是最终的替代者)。如果将相同的方法添加到Join,那么歧义将不再是问题,因为最终的替代程序将是Join中定义的成员函数,因此编译器能够生成虚拟表。

    [*]大多数编译器不会在此处写入空指针,而是指向通用函数的指针,该函数将打印错误消息并用terminate编码应用程序,从而比普通的段错误提供更好的诊断能力。

    09-08 09:04