为什么抽象类有一个虚函数表?
Why does an abstract class have a vtable?
关于这篇文章:
对于使用vtable的实现,答案是:通常是。你可能会认为抽象类不需要虚表,因为派生类将有自己的虚函数表,但在期间需要它。构造:在构造基类时,它设置虚表指针指向自己的虚表。之后当派生类如果输入构造函数,它将使用自己的虚函数表。
我假设答案是正确的,但我不太明白。为什么构造需要虚表?
因为标准是这么说的。
[class.cdtor]/4
基本原理是首先构造基类,然后派生类。如果在基类的构造函数内部调用虚函数,那么调用派生类是不好的,因为派生类还没有初始化。对象直接或间接调用虚函数时构造函数或析构函数,包括在构造或期间类的非静态数据成员的销毁,以及对象的调用应用的对象是正在构造的对象(称之为x)还是类中调用的函数是最后的重写构造函数或析构函数的类,而不是在派生类。
记住抽象类可以有非纯虚函数。此外,出于调试目的,最好将纯虚函数指向调试陷阱(例如,MSVC调用_purecall()
)。
如果所有虚函数都是纯虚函数,那么在MSVC中可以用__declspec(novtable)
省略虚函数表。如果您使用大量的接口类,这可以节省大量的开销,因为您省略了vfptr初始化。但是,如果不小心调用了纯虚函数,就会遇到难以调试的访问冲突。
变量表是c++中的实现问题,它们不是标准的一部分。
虚表既用于方法的动态调度,也用于RTTI。在纯抽象类中,nullptr虚表指针可以用于动态调度(因为虚表指针仅在具有该类型的实例时使用),而dynamic_cast
指向纯抽象类是合法的,并且可能需要虚表本身存在。
c++实现和ABI的设计者可以简单地给纯抽象类(没有实现方法的类,只有=0
方法)一个变量表,以使他们的实现更简单。每个类都有一个虚值表,虚值表指针在该类构造期间被设置。这样,代码就可以依赖虚函数表指针存在的事实,而不必每次都检查是否为空。代码不需要问"这是一个纯抽象类吗?"。
对于非纯抽象类(其中一些方法具有实现,但有些方法是纯虚的),在构造/销毁期间,您可以定义(如果意外)行为,该行为涉及调用该类的给定方法版本,而不是基类方法或继承方法。为此,您需要设置一个虚表。对于一个纯抽象类,这样的调用没有定义的结果,所以虚函数表是多余的,但是对于一个不是完全抽象的抽象类,这就不成立了。
当你的类有一个纯虚函数时,这并不意味着你不能有它的实现(!!)。这意味着你可以有一个抽象类,它也是完全实现的。抽象类的构造函数必须能够调用迄今为止为其存在的所有函数——甚至是纯虚函数。
如果你替换了客户端,你会得到基类构造函数的不同行为,这取决于派生类-不是一个好主意,所以这是不允许的。你可以不使用虚函数表,并静态解析所有函数调用——这是可行的,但这意味着要处理构造函数,而不是处理所有其他函数,并且需要内联所有其他函数来做到这一点(因为从构造函数调用的函数也可能调用虚函数等)——不太实用。
所以它只是为构造函数和析构函数实现了一个虚参表,以便在构造和析构过程中使用。它允许您在c'tor和d'tor中使用typeid和dynamic_cast来获得可预测的结果,并从您拥有的虚函数中获得可靠的行为。没有其他解决方案可以做到这一点。
- 有没有办法按值将纯抽象类的所有子类传递给 C++ 中的函数?
- 将自定义函数传递到基抽象类中以延迟执行
- C ++如何在原始抽象类中创建一个函数,该函数接受派生类的输入
- 从抽象类继承以创建另一个抽象类时,我应该重新声明所有虚函数吗?
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 抽象类和构造函数
- C++ 将一组对象传递给抽象类的成员函数
- 抽象类析构函数与继承"Shutdown"函数
- 尝试在 std::map 中插入抽象类时没有用于调用的匹配函数
- C++的抽象类继承和构造函数的问题
- C++ abort() 在函数内的抽象类对象指针调用上
- 具有继承类和函数的抽象类写入单独的向量c++
- C++ 抽象类构造函数调用
- 为什么不能将std :: async用于接收对抽象类作为参数的函数
- 抽象类和虚拟构造函数的替代方案
- 抽象类的复制构造函数中的问题
- C++ 接受对抽象类的常量引用的构造函数无法初始化 std::map
- 抽象类对象和纯虚拟析构函数
- 无法绑定 c++ 中抽象类中的函数
- 本地抽象类的纯虚拟析构函数