我刚刚在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)”可以是一种强大的技术,用于自定义多态类的行为。
我的问题是:
最佳答案
简单的解释是,由于Base
和Der1
中的Der2
继承都是虚拟的,因此在派生程度最高的对象Join
中只有一个对象实例。在编译时,并假设(这是常见情况)虚拟表作为调度机制,在编译Der1::foo
时,它将通过vtable将调用重定向到bar()
。
现在的问题是,编译器如何为每个对象生成vtable,Base
的vtable将包含两个空指针,Der1
的vtable将包含Der1::foo
和空指针,Der2
的vtable将包含空指针和Der2::bar
[* ]
现在,由于在上一级具有虚拟继承性,因此编译器在处理Join
时将创建单个Base
对象,从而为Base
的Join
子对象创建单个vtable。它有效地合并了Der1
和Der2
的vtable,并生成了一个包含指向Der1::foo
和Der2::bar
的指针的vtable。
因此,Der1::foo
中的代码将通过Join
的vtable调度到最终的替代程序,在这种情况下,替代程序在虚拟继承层次结构的另一个分支中。
如果添加Der3
类,并且该类定义了两个虚函数,则编译器将无法干净地合并这三个vtable并会提示,并且会出现一些与乘法定义方法的歧义有关的错误(没有覆盖程序)可以被认为是最终的替代者)。如果将相同的方法添加到Join
,那么歧义将不再是问题,因为最终的替代程序将是Join
中定义的成员函数,因此编译器能够生成虚拟表。
[*]大多数编译器不会在此处写入空指针,而是指向通用函数的指针,该函数将打印错误消息并用terminate
编码应用程序,从而比普通的段错误提供更好的诊断能力。