12.2 多重继承(2)
根据图12-7所示,对象SofaBed占用的内存空间大小为0x18字节。这些数据的内容是什么?它们又是如何存放在内存中的?具体如图12-8所示。
|
| (点击查看大图)图12-8 对象SofaBed的内存信息 |
如图12-8所示,对象SofaBed的首地址在0x0012FF5C处,在图中可看到子类的数据成员和两个父类中的数据成员。数据成员的排列顺序由继承父类的先后顺序所决定,从左向右依次排列。除此之外,还剩余两个地址值,分别为0x00426198与0x0042501C,这两个地址处的数据如图12-9所示。
|
| (点击查看大图)图12-9 子类对象的虚表指针对应的虚表信息 |
图12-9中显示了Debug下两个虚表指针所指向的虚表信息。查看图12-9中的两个虚表信息后会发现,这两个虚表中保存了子类的虚函数与父类的虚函数,父类的这些虚函数都是在子类中没有实现的。由此可见,编译器将子类CSofaBed的虚函数制作了两份。为什么会产生两份虚函数呢?我们先从对象SofaBed的构造入手,循序渐进地进行分析,过程如代码清单12-9所示。
代码清单12-9 对象SofaBed的构造过程—Debug版
- // 源码参考见代码清单12-7
- CSofaBed SofaBed; // 定义对象
- 0040F72D lea ecx,[ebp-24h] ; 传递this指针
- 0040F730 call @ILT+10(CSofaBed::CSofaBed) (0040100f) ; 调用构造函数
- // 分析构造函数CSofaBed
- CSofaBed(){
- ; 部分代码分析略
- 004011FE pop ecx ; 还原this指针
- 004011FF mov dword ptr [ebp-10h],ecx
- 00401202 mov ecx,dword ptr [ebp-10h] ; 以对象首地址作为this指针
- 00401205 call @ILT+110(CSofa::CSofa) (00401073) ; 调用沙发父类的构造函数
- 0040120A mov dword ptr [ebp-4],0
- 00401211 mov ecx,dword ptr [ebp-10h]
- 00401214 add ecx,8 ; 将this指针调整到第二个虚表指针地址处
- 00401217 call @ILT+130(CBed::CBed) (00401087) ; 调用床父类的构造函数
- 0040121C mov eax,dword ptr [ebp-10h] ; 获取第二个虚表指针地址
- ; 设置虚表指针
- 0040121F mov dword ptr [eax],offset CSofaBed::'vftable' (00426198)
- 00401225 mov ecx,dword ptr [ebp-10h] ; 获取对象的首地址
- ; 设置虚表指针
- 00401228 mov dword ptr [ecx+8],offset CSofaBed::'vftable' (0042501c)
- ; 部分代码分析略
- 0040125D ret
在代码清单12-9的子类构造中,根据继承关系的顺序,首先调用了父类CSofa的构造函数。在调用另一个父类CBed时,并不是直接将对象的首地址作为this指针传递,而是向后调整了父类CSofa的大小,以调整后的地址值作为this指针,最后再调用父类CBed的构造函数。
由于有了两个父类,因此子类在继承时也将它们的虚表指针一起继承了过来,也就有了两个虚表指针。可见,在多重继承中,子类虚表指针的个数取决于所继承的父类的个数,有几个父类便会出现对应个数的虚表指针(虚基类除外,详见12.3节的讲解)。
这些虚表指针在将子类对象转换成父类指针时使用,每个虚表指针对应着一个父类,如代码清单12-10所示。
代码清单12-10 多重继承子类对象转换为父类指针
- CSofaBed SofaBed;
- CSofa *pSofa = &SofaBed;
- 0040F73C lea eax,[ebp-24h] ; 直接将首地址转换为父类指针
- 0040F73F mov dword ptr [ebp-28h],eax
- CBed *pBed = &SofaBed;
- 0040F742 lea ecx,[ebp-24h]
- 0040F745 test ecx,ecx ; 检查对象首地址
- 0040F747 je main+51h (0040f751)
- 0040F749 lea edx,[ebp-1Ch] ; 即lea edx, [ebp-24h+8h],调整为CBed的指针
- 0040F74C mov dword ptr [ebp-30h],edx
- 0040F74F jmp main+58h (0040f758)
- 0040F751 mov dword ptr [ebp-30h],0
- 0040F758 mov eax,dword ptr [ebp-30h]
- 0040F75B mov dword ptr [ebp-2Ch],eax ; 保存调整后的this指针