12.4 菱形继承(4)
代码清单12-15展示了子类CSofaBed的构造过程,它的特别之处是在调用时要传入一个参数。这个参数是一个标志信息。构造过程中要先构造父类,然后构造自己。CSofaBed的两个父类有一个共同的父类,如果没有构造标记,它们共同的父类将会被构造两次,因此需要使用构造标记来防止重复构造的问题,构造顺序如下:
CFurniture
CSofa(根据标记跳过CFurniture构造)
CBed(根据标记跳过CFurniture构造)
CSofaBed自身
CSofaBed也使用了构造标记,当CSofaBed也是父类时,这个标记将产生作用,跳过所有父类的构造,只构造自身。当标记为1时,则构造父类;当标记为0时,则跳过构造函数。构造时可以使用标记来防止重复构造,同样也不能出现重复析构的错误,那么这又如何实现呢?我们来看一下代码清单12-16。
代码清单12-16 菱形结构的子类析构
- // CSofaBed 调用析构代理函数,因为是编译器自动添加的,所以无源码对照
- CSofaBed::'vbase destructor':
- ; 部分代码分析略
- 00401AE9 pop ecx
- 00401AEA mov dword ptr [ebp-4],ecx
- 00401AED mov ecx,dword ptr [ebp-4]
- 00401AF0 add ecx,20h
- ; 调用CSofaBed的析构函数
- 00401AF3 call @ILT+90(CSofaBed::~CSofaBed) (0040105f)
- 00401AF8 mov ecx,dword ptr [ebp-4]
- 00401AFB add ecx,20h
- ; 调用祖父类的析构函数
- 00401AFE call @ILT+60(CFurniture::~CFurniture) (00401041)
- ; CSofaBed::~CSofaBed实现
- virtual ~CSofaBed(){
- ; 部分代码分析略
- 00401B5E pop ecx ; 还原this指针
- 00401B5F mov dword ptr [ebp-10h],ecx ; 调整this指针
- 00401B62 mov eax,dword ptr [ebp-10h]
- ; 设置自身虚表
- 00401B65 mov dword ptr [eax-20h],offset CSofaBed::'vftable' (00425034)
- 00401B6C mov ecx,dword ptr [ebp-10h]
- ; 设置自身虚表
- 00401B6F mov dword ptr [ecx-14h],offset CSofaBed::'vftable' (00425028)
- 00401B76 mov edx,dword ptr [ebp-10h]
- 00401B79 mov eax,dword ptr [edx-1Ch]
- 00401B7C mov ecx,dword ptr [eax+4]
- 00401B7F mov edx,dword ptr [ebp-10h]
- ; 设置自身虚表。到此为止,3个虚表指针设置完毕,执行析构函数内的代码
- 00401B82 mov dword ptr [edx+ecx-1Ch],offset CSofaBed::'vftable' (0042501c)
- 00401B8A mov dword ptr [ebp-4],0
- printf("virtual ~CSofaBed()\r\n");
- }
- 00401B9E mov eax,dword ptr [ebp-10h]
- 00401BA1 sub eax,20h ; 获取this指针
- 00401BA4 test eax,eax ; 检查this指针
- 00401BA6 je CSofaBed::~CSofaBed+83h (00401bb3)
- 00401BA8 mov ecx,dword ptr [ebp-10h]
- 00401BAB sub ecx,14h
- 00401BAE mov dword ptr [ebp-14h],ecx
- 00401BB1 jmp CSofaBed::~CSofaBed+8Ah (00401bba)
- 00401BB3 mov dword ptr [ebp-14h],0
- 00401BBA mov ecx,dword ptr [ebp-14h]
- 00401BBD add ecx,10h ; 调整this指针
- 00401BC0 call @ILT+75(CBed::~CBed) (00401050) ; 调用父类析构函数
- 00401BC5 mov dword ptr [ebp-4],0FFFFFFFFh
- 00401BCC mov ecx,dword ptr [ebp-10h]
- 00401BCF sub ecx,14h ; 调整this指针
- 00401BD2 call @ILT+125(CSofa::~CSofa) (00401082) ; 调用父类析构函数
- ; 部分代码分析略
- 00401BF1 ret
根据对代码清单12-16的分析可知,菱形结构中子类的析构函数执行流程并没有像构造函数那样使用标记来防止重复析构,而是将祖父类放在最后调用。先依次执行两个父类CBed和CSofa的析构函数,然后执行祖父类的析构函数。Release版下的原理也是如此,这里就不再重复分析了。