zoukankan      html  css  js  c++  java
  • C++浅析——虚函数的动态和静态绑定

    源自一道面试题,觉得很有意思

    class CBase
    {
    public:
            virtual        void PrintData(int nData = 111);
    };
    void        CBase::PrintData(int nData /* = 111 */)
    {
            printf("CBase::PrintData, nData = %d
    ", nData);
    }
    
    class CDerived : public CBase
    {
    public:
            void PrintData(int nData = 222);
    };
    void        CDerived::PrintData(int nData /* = 111 */)
    {
            printf("CDerived::PrintData, nData = %d
    ", 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/
  • 相关阅读:
    二叉树的最大距离
    MS CRM 2011 RibbonExport Utility下载以及实用说明
    MS CRM 2011中的解决方案——使用
    MS CRM 2011的自定义与开发(5)——关系编辑器
    MS CRM 2011 RC中的新特性(5)——定期约会
    MS CRM2011中的事件脚本——入门
    MS CRM 2011 汇总更新 3
    MS CRM 4中模拟PartyList字段的方法
    MS CRM 2011的自定义与开发(4)——属性编辑器
    MS CRM 2011中的解决方案Solution_简介
  • 原文地址:https://www.cnblogs.com/organic/p/5005654.html
Copyright © 2011-2022 走看看