单继承的内存布局比较简单,虚函数表(确切地说叫虚表,因为表里除了虚函数外也会存别的内容)如果有的话也只有一个。
先来看没有虚函数的内存布局,
则用上篇介绍的命令,在VS里可以输出如下布局,
可以看到开始是基类部分的内存,接着是子类的成员。注意:成员函数不占内存。
下面修改成有虚函数的情况,
先来看基类的布局。可以看到开始位置是一个指针,该指针指向虚表。而在虚表里,有一个虚函数Base::Fun, 前面的0表示第一个虚函数,后面如果有的话以此类推,1,2,3。。。
子类布局如下。开始也是虚表指针,此时基类与子类共用一个虚表。接着是基类的成员部分,然后是子类的成员部分。在虚表里,可以看到有两个函数,第一个是FUN函数,由于子类覆盖了基类的FUN,于是可以看到指向的是Base1::Fun即子类的FUN函数,下面一个是子类定义的第二个虚函数。
有了以上的结构支持,当我们用基类的指针指向子类对象时,才会正确的调用到子类的函数。比如Base* obj = new Base1, 则在Base1的构造函数里,会用子类的FUN 替换掉基类的FUN,当Base1*转换成Base*的时候也不会影响到这个表,所以当obj->FUN的时候,就会找到正确的函数地址。
因为vfptr是一个指针,指向一个虚表,即元素为函数地址的数组,所以可以用以下代码来调用FUN,FUN1,