【问题标题】:Why does an abstract class have a vtable?为什么抽象类有一个 vtable?
【发布时间】:2017-01-16 06:45:09
【问题描述】:

关于this发帖:

对于使用 vtable 的实现,答案是:是的,通常情况下。你 可能认为抽象类不需要 vtable,因为 派生类将有自己的 vtable,但在期间需要它 构造:在构造基类时,它会设置 vtable 指向它自己的 vtable 的指针。后来当派生类 输入了构造函数,它会使用自己的vtable代替。

我假设答案是正确的,但我不太明白。为什么构建需要 vtable?

【问题讨论】:

  • “是的,通常”,并且在引用的文本之后,他显示了一个视觉指令来删除它。
  • 这样,如果构造函数调用一个虚方法,它会调用它自己的实现,而不是派生类的。

标签: c++ constructor abstract-class vtable pure-virtual


【解决方案1】:

当你的类有一个纯虚函数时,这并不意味着你不能也有它的实现(!!)。这意味着您可以拥有一个抽象类,该类也已完全实现。您的抽象类的构造函数必须能够调用迄今为止存在的所有函数 - 甚至是纯虚拟函数。

如果您已经替换了客户端,则基类构造函数的行为会根据派生类而有所不同——这不是一个好主意,因此这是不允许的。您可以不放置 vtable 并静态解析所有函数调用 - 这有效,但它意味着与所有其他函数相比专门处理构造函数,并且需要内联所有其他函数来执行此操作(因为从构造函数调用的函数也可能调用虚拟等) - 不是很实用。

所以它只是实现了一个 vtable 供构造函数和析构函数在构造和销毁期间使用。它允许您在 c'tor 和 d'tor 中使用 typeid 和 dynamic_cast 并获得可预测的结果,并从您拥有的虚函数中获得可靠的行为。没有其他解决方案可以做到这一点。

【讨论】:

  • 我认为这根本不是暗示。纯虚函数都可以有定义,但这并不意味着抽象类已经完全实现。它当然没有任何实际意义,因为它永远不能被实例化,以便它可以进行对成员函数的调用。具体派生类只需负责维护 vtable。真正的答案是因为标准说这就是它的工作原理。
  • 即使你提供了一个纯虚函数的定义,这并不意味着你可以虚拟调用一个纯虚函数!
【解决方案2】:

因为标准是这样说的。

[class.cdtor]/4

当一个虚函数被直接或间接调用时 构造函数或析构函数,包括在构造过程中或 销毁类的非静态数据成员,以及对象 该调用适用的是正在构建的对象(称为 x)或 销毁,调用的函数是最终的覆盖 构造函数或析构函数的类,而不是在 更多派生类。

基本原理是首先构造基类,然后构造派生类。如果在基类的构造函数中调用虚函数,那么调用派生类会很糟糕,因为派生类还没有初始化。

请记住,抽象类可能具有非纯虚函数。此外,出于调试目的,最好将纯虚函数指向调试陷阱(例如 MSVC 调用 _purecall())。

如果所有虚函数都是纯的,在 MSVC 中你可以省略带有__declspec(novtable) 的 vtable。如果你使用了很多接口类,这会导致显着的节省,因为你省略了 vfptr 初始化。但是如果你不小心调用了一个纯虚函数,你会很难调试访问冲突。

【讨论】:

    【解决方案3】:

    vtables 是 C++ 中的实现问题,它们不是标准的一部分。

    vtables 用于方法的动态分派和 RTTI。虽然 nullptr vtable 指针适用于纯抽象类中的动态调度(因为 vtable 指针仅在您有该类型的实例时使用),但纯抽象类的 dynamic_cast 是合法的,它可能需要vtable 本身存在。

    C++ 实现和 ABI 的设计者可能只是简单地为纯抽象类(没有实现方法的类,只有 =0 的类)提供了一个 vtable,以使其实现更简单。每个类都有一个 vtable,并且 vtable 指针在该类的构造过程中被设置。然后代码可以依赖于 vtable 指针存在并且不必每次都检查 null 的事实。代码不必问诸如“这是一个纯粹的抽象类”之类的问题。

    对于非纯抽象类(其中一些方法具有实现,但有些是纯虚拟的),在构造/销毁期间,您可以定义(如果意外)行为,涉及准确调用该类的给定方法的版本,而不是基类方法或继承方法。为此,您需要设置一个 vtable。对于纯抽象类,这种调用没有定义的结果,因此 vtable 是多余的,但对于不是完全抽象的抽象类,这不成立。

    【讨论】:

    • C++ 中没有“纯抽象”这样的东西。
    • @curiousguy 如果你不能实例化一个类(它至少有一个纯虚函数),它可以被称为抽象类。如果一个类只有纯虚函数,则可以称为纯抽象类。我承认,这些都不是 C++ 标准中的术语,但我不认为它们的使用有那么令人困惑。我已添加说明。
    • dynamic_cast 到纯抽象类是合法的”你能提供一个这样的强制转换的例子吗?
    • @curious struct A{ virtual void foo()=0; }; struct B { virtual ~B(){}; }; struct C: B,A { void foo()override{}; }; B* b=new C(); A* a= dynamic_cast<A*>(b); a->foo();?纯抽象类不隐含该代码,并对它进行动态转换吗?
    • dynamic_cast比较涉及的代码会使用基类B的vptr的类型信息找到派生最多的类C然后走继承树找到基类@ 987654327@;与继承树一起,运行时类型信息包含基类子对象的相对偏移量。
    猜你喜欢
    • 2017-03-09
    • 2014-10-30
    • 2019-05-30
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多