【问题标题】:Vtable placement of completely pure-virtual class完全纯虚拟类的 Vtable 放置
【发布时间】:2011-06-04 19:46:45
【问题描述】:

根据我对 C++ 规范的(有限)知识,具有虚拟成员的类的 vtable 放置在第一个非纯非内联虚方法的定义处。编译器如何处理从具有所有纯虚拟方法(例如接口)的类继承的类?这种情况下vtable放在哪里?

【问题讨论】:

  • C++ 规范本身没有提到 vtables。
  • 一个繁重的实现细节。 MSVC++ 有 __declspec(novtable) 来抑制这样的 vtable。和非标准的 __interface 关键字做同样的事情。

标签: c++ interface vtable pure-virtual


【解决方案1】:

vtable 存储实现的虚拟方法的地址。如果一个类的所有方法都是纯虚的并且没有实现,那么就不需要生成vtable。

如果没有一些派生自它并实现方法的类,您将无法长时间使用这样的类。每个实现了虚方法的类都有自己的单个虚表,其中包含所有虚方法的地址:它不以任何方式引用基类的虚表;地址重复。因此,如果您有一个从另一个类继承的类,那么该类将只使用它自己的 vtable。它不关心基类的vtable;这个 vtable 甚至不需要存在。

C++ 规范本身没有提到 vtables;它们只是一种已经变得普遍的编译器行为。

从 2020 年开始编辑:我差不多十年前写了这篇文章。我怀疑我是根据记忆和个人经验写的。下面的两个 cmets 表明编译器 确实 为基类创建 vtables(以抛出错误),尽管如果是这样我不知道您将如何为它们构造一个对象,并且某些编译器确实重用了 vtables基类。自 2011 年以来似乎没有人添加任何东西,而且我现在的认知能力下降了,很难再想太多,所以如果这里的一些细节至关重要,请自己做一些研究。

【讨论】:

  • 有趣。我会认为拥有重复的表会违反某种定义规则。
  • 没有一个表是重复的。仅当该类实现了虚拟方法时才会生成新表;这会生成一个唯一的 vtable,其中包含指向该实现的指针。该表将包含任何未更改的虚拟方法的重复地址。
  • 如果一个类的所有方法都是纯虚拟的,则没有一个实现这是不正确的。在 C++ 中,纯虚拟并不意味着没有实现。
  • 本,你是对的。我已经编辑了我的答案并投了你的票。
  • 实际上编译器会为带有错误存根的纯虚函数生成条目,即使它们没有用户定义的实现。这样做是为了在调用纯虚函数时捕获并报告错误。
【解决方案2】:

C++ 标准没有指定关于 vtable 放置的任何内容,甚至没有指定 v-table 的存在。它只是指定行为,而 v-table 恰好是最直接的实现,因此被广泛使用。

实际上,为抽象类存在 v-table 的一个原因是在构造和销毁期间使用,此时对象的动态类型是抽象类。

在一个只有纯虚函数的类中,显然不可能有构造函数(因为构造函数不能是虚函数)。但是,析构函数当然可以是虚拟的。

您的类仍然可以有一个带有实现的pure virtual destructor,然后需要 v-table(或等效的实现细节)。

但纯虚函数的实现很少见,在定义接口时不会这样做。

【讨论】:

    【解决方案3】:

    在您拥有实例之前,您不需要 vtable。

    【讨论】:

    • 什么实例?编译器不知道在编译单个 TU 时,纯虚函数是否会在另一个 TU 中定义实现。编译器实现者可以决定如何处理它,创建vtable并设置指向错误函数的指针以报告对未定义的纯虚函数的非法调用是一个合理的解决方案。
    • 我写这篇文章已经有一段时间了(由编辑通知),但我想我的意思是:要么它是抽象的(具有纯虚拟方法),因此无法实例化并且不需要一个 vtable,或者它不是,因此所有继承的纯虚拟方法都被覆盖。 OP 的案例是完全抽象的案例。在编译任何 TU 时,编译器应该知道方法是否是纯的,因为它包含在类声明中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 2010-11-21
    相关资源
    最近更新 更多