源自一道面试题,觉得很有意思
class CBase { public: virtual void PrintData(int nData = 111); }; void CBase::PrintData(int nData /* = 111 */) { printf("CBase::PrintData, nData = %d\n", nData); } class CDerived : public CBase { public: void PrintData(int nData = 222); }; void CDerived::PrintData(int nData /* = 111 */) { printf("CDerived::PrintData, nData = %d\n", nData); }
在main()中做如下调用:
CDerived oCDerived; CBase* pCBase = (CBase*)&oCDerived; pCBase->PrintData(); (*pCBase).PrintData(); oCDerived.PrintData();
大家先猜猜输出结果是什么?
是不是更奇怪,我们看看反汇编的代码:
14、oCDerived.PrintData(); push 0DEh lea ecx,[ebp-4] call @ILT+25(CDerived::PrintData) (0040101e) ;直接调用CDerived::PrintData(),无虚表取址过程 15、((CBase)oCDerived).PrintData(); mov esi,esp push 6Fh ;压入CBase::PrintData()形参 lea ecx,[ebp-4] push ecx ;压入oCDerived的this指针 lea ecx,[ebp-10h] call @ILT+10(CBase::CBase) (0040100f) ;调用CBase拷贝构造函数新创建了一个CBase对象 mov dword ptr [ebp-14h],eax mov edx,dword ptr [ebp-14h] mov eax,dword ptr [edx] ;取新CBase对象的虚表 mov ecx,dword ptr [ebp-14h] call dword ptr [eax] ;调用新CBase对象的虚表的第一个函数 15.1、CBase::CBase拷贝构造函数; mov dword ptr [ebp-4],ecx ;取this指针 mov eax,dword ptr [ebp-4] mov dword ptr [eax],offset CBase::`vftable' (00425024) ;虚表地址赋值,直接用的CBase虚表,而没有用CDerived的虚表 mov eax,dword ptr [ebp-4] ;将this指针给eax返回 16、pCDerived->PrintData(); mov esi,esp push 0DEh mov ecx,dword ptr [ebp-8] ;取pCDerived mov edx,dword ptr [ecx] ;取虚表 mov ecx,dword ptr [ebp-8] ;放入this指针 call dword ptr [edx] ;调用虚表的第一个函数,即PrintData()
从上述反汇编代码中可以看出:
1、oCDerived.PrintData()对象调用是静态绑定的
2、pCDerived->PrintData()指针调用是动态绑定的,而且(*pCDerived).PrintData(),即指针实例化后的对象调用也是动态绑定的(可自行看反汇编代码)
3、对象的类型转换中会参数新的对象,同时会调用新对象的构造拷贝函数,但由于CBase的默认拷贝构造函数为CBase::CBase(CBase&)形式,故仍会取CBase的虚表地址给新对象的虚表初始化,故((CBase)oCDerived).PrintData();实际上是调用了一个全新的CBase对象的PrintData()函数,因此不建议对对象进行类型转换,因为实际调用过程中已经不是原来的那个对象了,如果对象函数中涉及对成员的赋值或改动操作,那实际上是不会生效的。
本文为博主原创文章,如需转载请说明转至http://www.cnblogs.com/organic/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架