12.4 菱形继承(2)
代码清单12-13中一共定义了4个类,分别为CFurniture、CSofa、CBed和CSofaBed。CFurniture为祖父类,从CFurniture类中派生了两个子类:CSofa与CBed,它们在继承时使用了virtual的方式,即虚继承。
使用虚继承可以避免共同派生出的子类产生多义性的错误。那么,为什么要将virtual加在两个父类上而不是它们共同派生的子类呢?这个问题与现实世界中动物的繁衍很相似,例如,熊猫在繁衍时要避免具有血缘关系的雄性与雌性“近亲繁殖”,因为“近亲繁殖”的结果会使繁殖出的后代出现基因重叠的问题,从而造成残缺现象。类CBed与类CSofa就如同是一对兄妹,它们的父亲为CSofaBed,当类CBed与类CSofa“近亲结合”后“生下”存在基因问题的子类CSofaBed时,也会存在基因重叠问题,因此通过虚继承来防止这个问题的发生。接下来介绍菱形结构中子类CSofaBed的对象在内存中是如何存放的,如图12-13所示。
|
| 图12-13 CSofaBed的内存结构 |
图12-13中显示了SofaBed在内存中的信息,初步观察内存中保存的数据可得知,有些数据类似地址值。这些地址值都有哪些含义呢?图12-14对各个地址数据进行了注解。
|
| 图12-14 CSofaBed内存结构的注解 |
通过图12-14虽然可以知道各个数据所具有的含义,但是还存在一些模糊不清的数据无法理解,如CSofaBed_vt(new)和vt_offset,它们又都代表着什么呢?带着这个疑问,我们将代码清单12-13转换成汇编代码,如代码清单12-14所示。
代码清单12-14 菱形结构的虚表指针转换过程
- // C++(www.cppentry.com)源码对比,加入了父类指针的转换代码
- void main(int argc, char* argv[]){
- CSofaBed SofaBed;
- CFurniture * pFurniture = &SofaBed; // 转换成祖父类指针
- CSofa * pSofa = &SofaBed; // 转换成父类指针
- CBed * pBed = &SofaBed; // 转换成父类指针
- }
-
- // C++(www.cppentry.com)源码与对应汇编代码讲解
- void main(int argc, char* argv[]){
- CSofaBed SofaBed;
- 0040F718 push 1 ; 是否构造祖父类的标志,TRUE表示构造,FALSE表示不构造
- 0040F71A lea ecx,[ebp-28h] ; 传入对象的首地址作为this指针
- 0040F71D call @ILT+10(CSofaBed::CSofaBed) (0040100f) ; 调用构造函数
- CFurniture * pFurniture = &SofaBed;
- 0040F722 lea eax,[ebp-28h] ; 获取对象的首地址
- 0040F725 test eax,eax ; 检查代码
- 0040F727 jne main+32h (0040f732) ; 跳转到0x0040f732
- 0040F729 mov dword ptr [ebp-38h],0
- 0040F730 jmp main+3Fh (0040f73f)
- ; 取出对象的第二项数据vt_offset,此地址指向的数据如图12-14所示
- 0040F732 mov ecx,dword ptr [ebp-24h]
- 0040F735 mov edx,dword ptr [ecx+4] ; 取出偏移值后存入edx中
- 0040F738 lea eax,[ebp+edx-24h] ; 得到祖父类数据的所在地址
- 0040F73C mov dword ptr [ebp-38h],eax ; 利用中间变量保存祖父类的首地址
- 0040F73F mov ecx,dword ptr [ebp-38h]
- 0040F742 mov dword ptr [ebp-2Ch],ecx ; 赋值pFurniture
- CSofa * pSofa = &SofaBed;
- 0040F745 lea edx,[ebp-28h] ; 直接转换SofaBed对象的首地址为父类CSofa的指针
- 0040F748 mov dword ptr [ebp-30h],edx
- CBed * pBed = &SofaBed;
- 0040F74B lea eax,[ebp-28h] ; 获取对象SofaBed的首地址
- 0040F74E test eax,eax ; 地址检查
- 0040F750 je main+5Ah (0040f75a)
- 0040F752 lea ecx,[ebp-1Ch] ; 获取第二个CSofaBed_vt(new)指针
- 0040F755 mov dword ptr [ebp-3Ch],ecx
- 0040F758 jmp main+61h (0040f761)
- 0040F75A mov dword ptr [ebp-3Ch],0
- 0040F761 mov edx,dword ptr [ebp-3Ch]
- 0040F764 mov dword ptr [ebp-34h],edx ; 保存转换后的SofaBed地址到pSofa中
- }