关于this帖子:
对于使用vtable的实现,答案是:通常是。您 可能认为抽象类不需要vtable,因为 派生类将有自己的vtable,但在期间需要它 构造:在构建基类时,它设置 vtable指向自己的vtable的指针。后来派生类 输入构造函数,它将使用自己的vtable。
我假设答案是正确的,但我不太明白。为什么vtable需要完全用于施工?
答案 0 :(得分:3)
因为标准是这样说的。
直接或间接从a调用虚函数时 构造函数或析构函数,包括在构造期间或 破坏类的非静态数据成员,以及对象 调用适用的是正在构建的对象(称为x)或 破坏,所谓的函数是最终的覆盖者 构造函数或析构函数的类,而不是一个覆盖它的类 更多派生的类。
基本原理是首先构建基类,然后派生的基类。如果在基类内调用虚函数'构造函数,调用派生类是不好的,因为派生类尚未初始化。
请记住,抽象类可能具有非纯虚函数。此外,出于调试目的,最好将纯虚函数指向调试陷阱(例如,MSVC调用_purecall()
)。
如果所有虚拟功能都是纯粹的,那么在MSVC中,您可以使用__declspec(novtable)
省略vtable。如果使用大量接口类,这可以节省大量成本,因为省略了vfptr初始化。但是,如果您不小心调用了纯虚函数,那么您将很难调试访问冲突。
答案 1 :(得分:0)
vtables是C ++中的实现问题,它们不是标准的一部分。
vtable用于动态调度方法和RTTI。虽然nullptr vtable指针可用于动态分派(因为vtable指针仅在具有该类型的实例时使用)在纯抽象类中,但dynamic_cast
到纯抽象类是合法的,并且它可能要求vtable本身存在。
C ++实现和ABI的设计者可能只是简单地给出了纯抽象类(没有实现方法的类,只有=0
个)vtable,以使其实现更简单。每个类都有一个vtable,并且在构造该类时会设置vtable指针。然后,代码可以依赖于vtable指针存在的事实,并且不必每次都检查null。代码不必提出像#34这样的问题;这是一个纯粹的抽象类"。
对于非纯抽象类(其中某些方法具有实现但有些方法是纯虚拟的),在构造/销毁期间,您可以定义(如果是意外的)行为,该行为涉及完全调用此类的给定版本方法,而不是基类方法或继承方法。为此,您需要设置vtable。对于纯抽象类,没有这种调用的定义结果,因此vtable是多余的,但是对于一个不完全抽象的抽象类,这是不成立的。
答案 2 :(得分:-1)
当你的类具有纯虚函数时,这并不意味着你也不能拥有它的实现(!!)。这意味着你可以拥有一个抽象类,它也是完全实现的。抽象类的构造函数必须能够调用所有函数 - 即使是纯虚函数 - 因为这一点 - 到目前为止它仍然存在。
如果您已经替换了客户端,那么根据派生类,您将获得基类构造函数的不同行为 - 这不是一个好主意,因此不允许这样做。您可以放置没有vtable并静态解析所有函数调用 - 这是有效的,但它意味着处理构造函数与所有其他函数相比,并且需要内联所有其他函数来执行此操作(因为从构造函数调用的函数也可能调用虚拟等) - 不太实用。
因此它只是为构造函数和析构函数实现了一个vtable,以便在构造和销毁期间使用它。它允许您在c'tor和d'tor中使用typeid和dynamic_cast以及可预测的结果,并从您拥有的虚拟函数中获得可靠的行为。没有替代解决方案可以做到这一点。