2

我在课堂上被告知,在单继承的情况下,VTBL 包括类可以响应的所有虚函数。下图应说明这一点。 一张图片说明了这一点

在多重继承中,我被告知 VTBL 包括在该类中首先定义的所有虚函数或在该类中已被覆盖的虚函数。这意味着在运行时您必须使用分派算法搜索正确的方法实现。

我不完全确定为什么存在这种差异。为什么在多重继承的情况下 VTBL 不能包含类可以响应的所有虚函数(就像在单继承的情况下一样)?这应该会加快这个过程,因为我们不必在整个继承层次结构中在运行时寻找方法实现。

任何人都可以为我澄清这一点吗?

编辑:当我提到多继承的调度算法时,我指的是以下内容: 在此处输入图像描述 只是为了澄清:注意我们必须如何遍历层次结构来搜索实现,而不是仅仅去当前类的 VTBL 并调用跳转到方法。

4

2 回答 2

1

这是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?
} 

仅当覆盖并真正指向 a时,才需要传递给的指针参数g()需要偏移调整。编译器应该做什么?为调用生成代码时,Dmfpb2D

  • 它可能不知道D存在。(这就是动态多态性的重点:能够在不重新编译的情况下调用未来的代码)
  • 它不知道是否pb2指向 a D(它只知道仅在运行时)。

因为多态类需要对未来可能的进一步派生的无限集保持灵活,所以该问题通常通过以下方式解决

  • 创建处理偏移调整的特殊 vtbl。
  • 对于派生类对象,将新的 vptr 添加到这些 vtbl,在第一个基类之后为每个基类添加一个额外的 vptr。

将所有虚函数合并到一个表中会破坏这种灵活性。请注意,多重“并行”继承D: B1, B2 {};与“堆叠”继承不同D: M: B {};。后者需要一个替换链,前者有两个这样的链并且不B1兼容B2

于 2013-09-14T12:08:04.413 回答
0

如果您必须基类AB您的多重继承对象D,它们有自己的 vtable 布局,并且D需要提供与两者的 vtable 匹配的 vtableAB. 此外,如果另一个类派生自D并且可能源自另一个类似的多重继承类,那么同样的事情会再次发生,即需要多个 vtable。它们不能简单地合并。因此,多重继承的对象通常有多个 vtable,编译器会插入代码来首先确定函数的正确 vtable,然后调用它。我认为如果虚拟函数不在虚拟基类中,则基于指向具有多个基的对象的指针确定正确 vtable 的代码只是简单的加法或减法,否则查找虚拟基类的位置,即, 并没有做任何真正昂贵的事情,但需要的不仅仅是间接调用。

于 2013-09-14T11:36:08.020 回答