对于其clas(子)具有单继承的对象,通常需要多少个vptrs,其基类为多个继承base1和base2.确定对象提供了多少个vpt的策略是什么,它提供了几个单继承和多继承.虽然标准没有指定vptrs,但我只是想知道一个实现如何实现虚函数.
解决方法
你为什么在乎?简单的答案就足够了,但我想你想要更完整的东西.
这不是标准的一部分,因此任何实现都可以按照自己的意愿自由进行,但一般的经验法则是在使用虚拟表指针的实现中,作为第0个近似值,用于动态调度,最多需要许多指向虚拟表的指针,因为有些类将新的虚方法添加到层次结构中. (在某些情况下,可以扩展虚拟表,并且基本和派生类型共享一个vptr)
// some examples: struct a { void foo(); }; // no need for virtual table struct b : a { virtual foo1(); }; // need vtable,and vptr struct c : b { void bar(); }; // no extra virtual table,1 vptr (b) suffices struct d : b { virtual bar(); }; // extra vtable,need b.vptr and d.vptr struct e : d,b {}; // 3 vptr,2 for the d subobject and one for // the additional b struct f : virtual b {}; struct g : virtual b {}; struct h : f,g {}; // single vptr,only b needs vtable and // there is a single b
基本上每个需要自己动态调度的类型的子对象(不能直接重用父对象)都需要自己的虚拟表和vptr.
实际上编译器将不同的vtable合并到一个vtable中.当d在b中的函数集上添加一个新的虚函数时,编译器会通过将新的插槽附加到vtable的末尾将可能的两个表合并为一个表,因此d的vtable将是一个扩展版本b的vtable与末端的额外元素保持二进制兼容性(即d vtable可以解释为ab vtable以访问b中可用的方法),d对象将具有单个vptr.
在多重继承的情况下,事情变得有点复杂,因为每个基础需要与完整对象的子对象具有相同的布局,而不是它是单独的对象,因此将有额外的vptrs指向完整对象中的不同区域虚函数表.
最后,在虚拟继承的情况下,事情变得更加复杂,并且可能有多个vtable用于同一个完整对象,随着构造/破坏的发展,vptr会被更新(vptr总是在构造/破坏演变时更新,但没有虚拟继承vptr将指向base的vtable,而在虚拟继承的情况下,将有多个相同类型的vtable)