单重虚继承无虚函数
1 | class BaseClass |
在内存窗口中查看 myClass
1 | 0x0019FEF4 58 58 41 00 XXA. -----子类 |
可以看到此时的内存结构和非虚继承有差别,基类的内存位于子类的内存之下
此外在内存结构的顶端,出现了一个额外的指针,此指针指向的位置为基类的偏移表
打开0x00415858
1 | 0x00415858 00 00 00 00 .... |
首地址为保留区域,第二项所保存的内存值为 0x14 ,即为在子类内存中的基类区域到子类首地址的偏移量,也就是在第一个内存窗口中 0x0019FF04
- 0x0019FEF4
的值
单重虚继承有虚函数
父类有虚函数
1 | class BaseClass |
myClass
的内存结构为
1 | 0x0019FEF0 70 58 41 00 pXA.-----子类(基类偏移区指针) |
子类重写父类虚函数
1 | class BaseClass |
打开 myClass
的内存窗口
1 | 0x0019FEF0 70 68 41 00 phA. |
内存结构同上
子类添加虚函数
1 | class BaseClass |
打开 myClass
的内存
1 | 0x0019FEEC 68 68 41 00 hhA. ---子类虚函数指针 |
多重继承有虚函数
父类有虚函数
1 | class BaseClass1 |
打开 myClass
的内存窗口
1 | 0x0019FEFC 74 68 41 00 thA.---父类BaseClass1 的虚函数表指针 |
其中,父类 BaseClass1
的虚函数表中包含了 BaseClass2
的虚函数
打开 BaseClass1
的虚函数指针 0x00416874
1 | 0x00416874 2b 12 41 00 +.A. ---BaseClass1::fun1() |
只有一个父类有虚函数
1 | class BaseClass1 |
打开 myClass
的内存窗口
1 | 0x0019FF00 68 68 41 00 hhA. ---父类BaseClass2 的虚函数表指针 |
发现此时的内存顺序并不是按照继承顺序来排列,而是按照父类中有无虚函数的状态进行排序
即有虚函数的父类优先
子类重写父类的虚函数
子类重写的虚函数会覆盖父类虚表中的对应虚函数的位置,内存结构无差异
子类新加虚函数
1 | class BaseClass1 |
打开 myClass
的内存窗口
1 | 0x0019FEFC 70 68 41 00 phA. |
此时并未给子类添加虚函数表指针
打开第一父类 BaseClass1
的虚函数表指针
其中除了第一父类 BaseClass1
的虚函数,依次包含了子类新添加的虚函数,以及 BaseClass2
的虚函数
1 | 0x00416870 3f 12 41 00 ?.A. ---BaseClass1::func1() |
###
多重虚继承(菱形继承)无虚函数
1 | class BaseClass |
打开 sonClass
的内存窗口
1 | 0x0019FEF8 70 58 41 00 pXA.---父类FatherClassA 的偏移量指针 |
打开 FatherClassA
的偏移量指针 0x00415870
1 | 0x00415870 00 00 00 00 .... |
其中偏移值为 0x14
偏移值的含义为基类的内存到第一父类 FatherClassA 的偏移量
也就是 0x0019FF0C - 0x0019FEF8 = 0x00000014
打开 FatherClassB
的偏移量指针 0x0041587c
1 | 0x0041587C 00 00 00 00 .... |
其中偏移值为 0x0c
偏移值的含义为基类的内存到第二父类 FatherClassB 的偏移量
也就是 0x0019FF0C - 0x0019FF00= 0x0000000C
多重虚继承(菱形继承)有虚函数
只有虚基类有虚函数
1 | class BaseClass |
打开 sonClass
的内存窗口
1 | 0x0019FEF4 a0 68 41 00 ?hA.---父类FatherClassA 的偏移量指针 |
中间类重写虚基类的虚函数
此时允许两个中间类同时重写基类中的虚函数,因为中间类都是虚继承基类,相当于它们共享了唯一一份基类
所以如果同时重写无异于重复定义,在子类中会报错
仅允许其中一个中间类重写虚基类的虚函数
1 | class BaseClass |
内存结构同 只有虚基类有虚函数 的内存结构
中间类新加虚函数
1 | class BaseClass |
打开 sonClass
的内存窗口
1 | 0x0019FEEC b0 68 41 00 ?hA. ---父类FatherClassA的虚函数表指针 |
对比只有 只有虚基类有虚函数 的内存结构发现,仅仅只是在前者的基础上添加了每个类对应的虚函数表指针
但偏移指针所指向的偏移值有所变化
打开 FatherClassA 中的偏移表指针
1 | 0x00416920 fc ff ff ff ?... |
第二个值为 0x18 ,根据之前的推论经验,此为基类到首地址的偏移量
但现在的内存首地址为 0x0019FEEC ,而 0x0019FF08 - 0x18 为 0x0019FEF0
所以在此猜测偏移量 0x18 是基类到父类的偏移表指针的偏移量,不包括父类虚函数表指针
于是打开 FatherClassB 中的偏移指针 0x00416988 进行验证
1 | 0x00416988 fc ff ff ff ?... |
第二个值为 0x0c 正好为 0x0019FF08 - 0x0019FEFC 的值,猜想得到验证
子类重写虚基类的虚函数
内存结构无变化
子类重写中间类的虚函数
内存结构无变化,但重写的虚函数加入到了基类的虚函数表中
子类新加虚函数
1 | class BaseClass |
打开 sonClass
的内存窗口
1 | 0x0019FEEC ac 68 41 00 ?hA. ---父类FatherClassA的虚函数表指针 |
根据之前的经验,子类新添加的虚函数应该加入到第一父类中的虚函数表中
打开 FatherClassA 的虚函数表指针 0x004168ac
1 | 0x004168AC 21 12 41 00 !.A. |
打开汇编窗口
0x00411221
的确为 SonClass::func3()