我从这里(2.6.2)阅读了VTT的结构。但是,我不明白所有元素的目的。
主虚拟指针应指向 D 的主虚拟表(在 vtable 组中,对吗?
辅助 VTT包含在构造时提供给直接非虚拟基类(我们称之为 B)的 VTT。我的理解是,因为这些直接基类(B)可能继承自虚类,这些直接基类(B)应该给这些已经初始化的虚拟类一些其他的vtable,以"让他们相信"它们只是每个B的一部分。这是对的吗?
现在,最后 2 种类型的条目的目的是什么:辅助虚拟指针和虚拟 VTT?我知道继承层次结构中的虚拟类没有被赋予适当的最终 vtable,所以这可能是目的之一。此外,虚拟类可以从其他虚拟类继承自身(考虑 V1 继承自 V2)。然后 V2 再次需要一个特殊的 vtable 来"让它相信"它是 V1 的一部分(当 V1 正在构建时)。
如果您能解释VTT中每种类型条目的确切内容和目的,我将不胜感激。
Liskov 可替换性意味着派生类必须在基类可接受的任何位置可用。在C++中,这意味着您可以将Derived
对象的地址传递给采用Base*
的函数。
现在,在构造复杂对象的过程中,您可能会遇到这样的情况,即根据C++规则*this
是这样的派生对象(因为Derived
ctor 已成功运行),因此您必须能够*this
传递给期望Base*
的函数。
但是对于多重和虚拟继承,您需要额外的预防措施。Base*
必须指向Base
子对象。在 Itanium ABI 中,该基本子对象必须具有一个 vtable 指针,用于正确描述部分构造对象的行为。该行为取决于构造和未构造的部件。
虽然对象可以部分构造,但其粒度只是类级别。调用的派生最多的构造函数准确确定将运行哪些基类构造函数(这不依赖于运行时行为)。这些要么已经运行,要么没有运行。
因此,一个可行的实现是在每个基类子对象(可能重叠)中具有一组 vtable 和一组 vtable 指针,并在每个构造函数成功完成时设置 vtable 指针。
虚拟基类与虚函数没有明显的关系,但它们的名字来源于您可以在 vtables 中存储虚拟基类偏移量。当您将Derived* this
传递给采用Base*
的函数时,需要此偏移量,其中该Base
类型是Derived
的虚拟基类。(当它是常规基类时,您将使用固定偏移量,并且该偏移量甚至可能为零)