我在课堂上教过,在单继承的情况下,VTBL包括类可以响应的所有虚函数。下图应说明这一点。
在多重继承中,我被教导VTBL包括在该类中首先定义的所有虚函数或者在该类中已被覆盖的虚函数。这意味着在运行时您必须使用调度算法搜索正确的方法实现。
我不完全确定为什么存在这种差异。为什么在多重继承的情况下VTBL不能包含该类可以响应的所有虚函数(就像单继承的情况一样)?这应该加快进程,因为我们不必在整个继承层次结构中的运行时查找方法实现。
有人可以为我澄清这个吗?
编辑:当我引用多重继承的调度算法时,我指的是以下内容:
只是为了澄清:注意我们如何遍历层次结构以搜索实现,而不是仅仅转到当前类的VTBL并调用跳转到该方法。
答案 0 :(得分:1)
以下是published German notes by Scott Meyers的翻译示例。考虑
class B1 {
public:
virtual void mf(); // may be overridden in derived classes
};
class B2 {
public:
virtual void mf(); // may be overridden in derived classes
};
class D: public B1, public B2 {};
void g(B2 *pb2)
{
pb2->mf(); // requires offset adjustment before calling mf?
}
仅当g()
覆盖D
且mf
真正指向pb2
时,才需要传递给D
的指针参数进行偏移调整。编译器应该怎么做?为呼叫生成代码时,
D
存在。 (这是动态多态的要点:能够在不重新编译的情况下调用未来的代码)pb2
是否指向D
(它只在运行时才知道)。因为多态类需要保持灵活性,以抵抗未来可能的未来进一步推导,所以问题通常由
解决。将所有虚拟函数合并到一个表中会破坏这种灵活性。请注意,多个“并行”继承D: B1, B2 {};
与“堆叠”继承D: M: B {};
不同。后者需要一个替代链,前者有两个这样的链并且不兼容B1
和B2
。
答案 1 :(得分:0)
如果必须基于多重继承对象A
的基类B
和D
,则这些对象具有自己的vtable布局,D
需要提供与之匹配的vtable A
和B
的vtable。此外,如果另一个类派生自D
并且可能来自另一个类似的多个继承类,则同样的事情再次发生,即,需要多个vtable。它们不能简单地合并。因此,乘法继承的对象通常具有多个vtable,编译器首先插入代码以确定函数的正确vtable,然后调用它。我认为根据指向具有多个碱基的对象的指针确定正确的vtable的代码只是一个简单的加法或减法,如果虚函数不在虚拟基类中并且查找否则,虚拟基类的位置,即没有任何真正昂贵的工作,但不仅仅需要间接调用。