快速业务通道

C++对象布局及多态实现探索之虚函数调用

作者 佚名技术 来源 程序设计 浏览 发布时间 2012-06-30
的,只与具体的对象相关,因为虚指针是存放在具体的对象中,而虚表只和对象的类型相关。这也就是多态会发生的原因。

请回忆一下前面讨论过的C071类,当子类重写从父类继承的虚函数时,子类的虚表内容的变化,及和父类虚表内容的区别(请参照第二篇中打印的子类和父类的虚表信息)。具体的通过指向子类对象的父类指针来调用被子类重写过的虚函数时的调用过程,请有兴趣的朋友自己调试一下,这里不再列出。

另外前面在《C++对象布局及多态实现之动态和强制转换》中我们讨论了指针的类型动态转换。我们在这里再利用C041、C042及C051类,来看看指针的类型动态转换。这几个类的定义请参见前文。类C051从C041和C042多重继承而来,且后两个类都有虚函数。执行如下代码:

C051 obj;
C041 * pt1 = dynamic_cast<C041*>(&obj);
C042 * pt2 = dynamic_cast<C042*>(&obj);
pt1->foo();
pt2->foo2();

第一个动态转型对应的汇编代码为:

00404B59 lea eax,[ebp+FFFFF8ECh]
00404B5F mov dword ptr [ebp+FFFFF8E0h],eax

因为不需要调整指针位置,所以很直接,取出对象的地址后直接赋给了指针。

第二个动态转型牵涉到了指针位置的调整,我们来看看它的汇编代码:

01 00404B65 lea eax,[ebp+FFFFF8ECh]
02 00404B6B test eax,eax
03 00404B6D je 00404B7D
04 00404B6F lea ecx,[ebp+FFFFF8F1h]
05 00404B75 mov dword ptr [ebp+FFFFF04Ch],ecx
06 00404B7B jmp 00404B87
07 00404B7D mov dword ptr [ebp+FFFFF04Ch],0
08 00404B87 mov edx,dword ptr [ebp+FFFFF04Ch]
09 00404B8D mov dword ptr [ebp+FFFFF8D4h],edx

代码要复杂的多。&obj运算后得到的是一个指针,前三行指令就是判断这个指针是否为NULL。奇怪的是第4行并没有根据eax中的地址(即对象的起始地址)来进行指针的位置调整,而是直接把[ebp+FFFFF8F1h]的地址取到ecx寄存器中。第1行指令中的[ebp+ FFFFF8ECh]实际是得到对象的地址,ebp所加的那个数实际是个负数(补码)也就是对象的偏移地址。对比两个数发现相差5字节,这样实际上第4行是直接得到了指针调整后的地址,即将指针指向了对象中的属于C042的部分。后面的代码又通过一个临时变量及edx寄存器把调整后的指针值最终存到了 pt2指针中。

这些代码实际可以优化成二行:

lea eax, [ebp+FFFFF8F1h]
mov dword ptr [ebp+FFFFF8d4h], eax

我们曾提到C051类有两个虚表,相应对象中有也两个虚表指针,之所以不合并为一个,就是为了处理指针的类型动态转换。结合前面对于多态的讨论,我们就可以理解得更清楚了。pt2->foo2();调用时,对象的类型还是C051,但经过指针动态转换pt2指向了对象中属于C042的部分的起始,也就是第二个虚表指针。这样在进行函数调用时就不需要再做额外的处理了。我们看看pt1->foo();及pt2->foo2 ();产生的汇编码即知。

01 00404B93 mov eax,dword ptr [ebp+FFFFF8E0h]
02 00404B99 mov edx,dword ptr [eax]
03 00404B9B mov esi,esp
04 00404B9D mov ecx,dword ptr [ebp+FFFFF8E0h]
05 00404BA3 call dword ptr [edx]
06 00404BA5 cmp esi,esp
07 00404BA7 call 0041DDDE
08 00404BAC mov eax,dword ptr [ebp+FFFFF8D4h]
09 00404BB2 mov edx,dword ptr [eax]
10 00404BB4 mov esi,esp
11 00404BB6 mov ecx,dword ptr [ebp+FFFFF8D4h]
12 00404BBC call dword ptr [edx]
13 00404BBE cmp esi,esp
14 00404BC0 call 0041DDDE

前7行为pt1->foo();,后7行为pt2->foo2();。唯一不同的是指针指向的地址不同,调用机制是一样的。

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号