关于这篇文章:
对于使用虚函数表的实现,答案是: 是的,通常需要。你可能认为抽象类不需要虚函数表,因为派生类将有自己的虚函数表,但实际上在构建过程中仍然需要虚函数表。因为当基类被构造时,它会将虚函数表指针设置为自己的虚函数表。稍后,当进入派生类构造函数时,它将使用自己的虚函数表。我认为答案是正确的,但我不太明白为什么构造函数确切地需要虚函数表。
_purecall()
)是很好的做法。__declspec(novtable)
省略vtable。如果您使用了大量接口类,这可能会导致显著的节省,因为您省略了vfptr初始化。但是,如果您意外调用了纯虚函数,您将遇到难以调试的访问冲突问题。vtables是C++中的实现问题,它们不是标准的一部分。
vtables用于方法的动态调度和RTTI。虽然在纯抽象类中,nullptr vtable指针可以用于动态调度(因为只有在具有该类型实例时才使用vtable指针),但对纯抽象类进行dynamic_cast
是合法的,这可能需要vtable本身存在。
C++实现和ABI的设计者可能只是给纯抽象类(一个没有实现方法,只有=0
的类)一个vtable,以使他们的实现更简单。每个类都有一个vtable,并且vtable指针在该类的构造过程中设置。代码可以依赖于vtable指针存在的事实,而不必每次都检查null。代码不必问诸如“这是一个纯抽象类吗”的问题。
dynamic_cast
代码将使用基类 B
的 vptr 类型信息来查找最派生类 C
,然后遍历继承树以查找基类 A
;连同继承树一起,运行时类型信息包含基类子对象的相对偏移量。 - curiousguy当你的类有一个纯虚函数时,这并不意味着你不能为它编写实现代码。因此,你可以拥有一个抽象类,它也是完全实现的。你的抽象类的构造函数必须能够调用所有函数,即使是到目前为止存在的纯虚函数,因为这一点。
如果你替换了客户端,那么基类构造函数的行为将取决于派生类,这不是一个好主意,所以不允许这样做。你可以不使用虚表,并静态解析所有函数调用,这样可以工作,但它意味着与所有其他函数相比,需要特别处理构造函数,并且需要内联所有其他函数来完成这个过程(因为从构造函数调用的函数可能还会调用虚函数等)- 这不是非常实用。
因此,它只是为构造函数和析构函数实现了一个虚表,在构造和销毁期间使用。它允许你在构造函数和析构函数中使用typeid和dynamic_cast,并获得可预测的结果,并从你拥有的虚函数中获得可靠的行为。没有其他替代方案可以做到这一点。