单一继承
在单继承的情况下,带有虚函数的每个类只含有一个虚函数表。而每个类的对象也只含有一个虚表指针。由于派生类和基类中的虚函数在虚函数表中的偏移位置是相同的,因此在编译期只需要指定其偏移值即可,在运行期通过虚表指针和偏移值即可在虚函数表中找到对应位置的虚函数。
多重继承
class Base1 {/*含有虚函数*/};
class Base2 {/*含有虚函数*/};
class Derive : public Base1, public Base2 {};
Base1 *p1 = new Derive;
Base2 *p2 = new Derive;
上述多继承的情况下,派生类对象的虚表指针又是怎样的呢?首先对于p1应该毫无争议,其和单继承情况完全一致。那么对于p2呢?虽然也能实现多态,但其实并没有表面上那么简单,编译器为它操碎了心。在编译期会转换成如下代码:
Derive *tmp = new Derive;
Base2* p2 = tmp? tmp + sizeof(Base1) : 0;
可见该基类指针增加了一个Base1大小的偏移,主要原因在于最左的数据成员属于Base1而不属于Base2。那么现在来思考下,这种情况下,对于派生类的对象而言,一个虚表和一个虚表指针可以实现多态吗?答案是否定的,因为在只有一个虚表的情况下,对于派生类中Base2的虚函数而言,其在虚表中的偏移值不是从0开始的,而Base2本身作为父类,其虚表的起始偏移值是0,这样子类和父类的虚函数在虚表中偏移值就会不同,所以没法实现运行期多态。而解决方法就是再增加一个虚表和一个虚表指针。这样可以保证每个虚函数不论是在基类,还是在派生类中,其在虚表中的位置都是唯一确定且相同的。结论:派生类有n个父类,就有n个虚表,其对象就有n个虚表指针。