zoukankan      html  css  js  c++  java
  • c++ 反汇编 虚函数

      虚函数是面向对象程序设计的关键组成部分。对于具有虚函数的类而言,构造函数和析构函数的识别流程更加简单。而且,在类中定义了虚函数之后,如果没有提供默认的构造函数,编译器必须提供默认的构造函数。
      对象的多态性需要通过虚表和虚表指针来完成,虚表指针被定义在对象首地址的前4字节处,因此虚函数必须作为成员函数使用。由于非成员函数没有this指针,因此无法获得虚表指针,进而无法获取虚表,也就无法访问虚函数。

    class CVirtual
    {
    public:
        ~CVirtual()
        {
            printf("~CVirtual");
        }
        CVirtual()
        {
        }
        CVirtual(int nNumber)
        {
            m_nNumber = nNumber;
        }
        virtual int GetNumber()
        {
            return m_nNumber;
        }
        virtual void SetNumber(int nNumber)
        {
            m_nNumber = nNumber;
        }
    private:
        int m_nNumber;
    };

    类中只定义一个int类型的数据成员,类大小却为8。当类中含有虚函数时,对象空间中第一项存放虚表指针。

    35:     // 获取含有虚函数表的类大小
        36:     int nSize = sizeof(CVirtual);
    0007739D C7 45 EC 08 00 00 00 mov         dword ptr [nSize],8  

    虚表在构造函数中设置

    13:     CVirtual()
    00077170 55                   push        ebp  
    00077171 8B EC                mov         ebp,esp  
    00077173 81 EC CC 00 00 00    sub         esp,0CCh  
    00077179 53                   push        ebx  
    0007717A 56                   push        esi  
    0007717B 57                   push        edi  
    0007717C 51                   push        ecx  
    0007717D 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh]  
    00077183 B9 33 00 00 00       mov         ecx,33h  
    00077188 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
    0007718D F3 AB                rep stos    dword ptr es:[edi]  
    0007718F 59                   pop         ecx  
    00077190 89 4D F8             mov         dword ptr [this],ecx  
    00077193 8B 45 F8             mov         eax,dword ptr [this]  //取this指针
    00077196 C7 00 54 2E 11 00    mov         dword ptr [eax],offset CVirtual::`vftable' (0112E54h)//设置对象第一项为虚表。  
        14:     {
        15:     }
    0007719C 8B 45 F8             mov         eax,dword ptr [this]  
    0007719F 5F                   pop         edi  
    000771A0 5E                   pop         esi  
    000771A1 5B                   pop         ebx  
    000771A2 8B E5                mov         esp,ebp  
    000771A4 5D                   pop         ebp  
    000771A5 C3                   ret  

    析构时也要还原自身虚表

     9:     ~CVirtual()
        10:     {
    000771C0 55                   push        ebp  
    000771C1 8B EC                mov         ebp,esp  
    000771C3 81 EC CC 00 00 00    sub         esp,0CCh  
    000771C9 53                   push        ebx  
    000771CA 56                   push        esi  
    000771CB 57                   push        edi  
    000771CC 51                   push        ecx  
    000771CD 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh]  
    000771D3 B9 33 00 00 00       mov         ecx,33h  
    000771D8 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
    000771DD F3 AB                rep stos    dword ptr es:[edi]  
    000771DF 59                   pop         ecx  
    000771E0 89 4D F8             mov         dword ptr [this],ecx  
    000771E3 8B 45 F8             mov         eax,dword ptr [this]  
    000771E6 C7 00 54 2E 11 00    mov         dword ptr [eax],offset CVirtual::`vftable' (0112E54h)  
        11:         printf("~CVirtual");
    000771EC 68 60 2E 11 00       push        offset string "~CVirtual" (0112E60h)  
    000771F1 E8 CF A1 FF FF       call        _printf (0713C5h)  
    000771F6 83 C4 04             add         esp,4  
        12:     }
    000771F9 5F                   pop         edi  
    000771FA 5E                   pop         esi  
    000771FB 5B                   pop         ebx  
    000771FC 81 C4 CC 00 00 00    add         esp,0CCh  
        12:     }
    00077202 3B EC                cmp         ebp,esp  
    00077204 E8 92 BC FF FF       call        __RTC_CheckEsp (072E9Bh)  
    00077209 8B E5                mov         esp,ebp  
    0007720B 5D                   pop         ebp  
    0007720C C3                   ret  

    使用对象直接调用自身虚函数,不会间接访问虚表调用。

    40:     MyVirtual.SetNumber(argc);
    002673C1 8B 45 08             mov         eax,dword ptr [argc]  
    002673C4 50                   push        eax  
    002673C5 8D 4D DC             lea         ecx,[MyVirtual]  
    002673C8 E8 23 A2 FF FF       call        CVirtual::SetNumber (02615F0h)  
    41:     printf("%d
    ", MyVirtual.GetNumber());
    002673CD 8D 4D DC             lea         ecx,[MyVirtual]  
    002673D0 E8 FF B3 FF FF       call        CVirtual::GetNumber (02627D4h)  
    002673D5 50                   push        eax  
    002673D6 68 6C 2E 30 00       push        offset string "%d
    " (0302E6Ch)  
    002673DB E8 E5 9F FF FF       call        _printf (02613C5h)  
    002673E0 83 C4 08             add         esp,8 

    当使用对象指针,引用时需要使用虚表。

        43:     p = &MyVirtual;
    001773E3 8D 45 DC             lea         eax,[MyVirtual]  
    001773E6 89 45 D0             mov         dword ptr [p],eax  
        44:     p->SetNumber(66);
    001773E9 8B F4                mov         esi,esp  
    001773EB 6A 42                push        42h  
    001773ED 8B 45 D0             mov         eax,dword ptr [p]  //eax 存对象地址
    001773F0 8B 10                mov         edx,dword ptr [eax]//EDX 存虚表指针  
    001773F2 8B 4D D0             mov         ecx,dword ptr [p]  
    001773F5 8B 42 04             mov         eax,dword ptr [edx+4] //取虚表第二项,即SetNumber函数 
        44:     p->SetNumber(66);
    001773F8 FF D0                call        eax  
    001773FA 3B F4                cmp         esi,esp  
    001773FC E8 9A BA FF FF       call        __RTC_CheckEsp (0172E9Bh)  
        45:      printf("%d
    ", p->GetNumber());
    00177401 8B 45 D0             mov         eax,dword ptr [p]  
    00177404 8B 10                mov         edx,dword ptr [eax] //edx存虚表指针 
    00177406 8B F4                mov         esi,esp  
    00177408 8B 4D D0             mov         ecx,dword ptr [p]  
    0017740B 8B 02                mov         eax,dword ptr [edx] //虚表第一项 
    0017740D FF D0                call        eax  
    0017740F 3B F4                cmp         esi,esp  
    00177411 E8 85 BA FF FF       call        __RTC_CheckEsp (0172E9Bh)  
    00177416 50                   push        eax  
    00177417 68 6C 2E 21 00       push        offset string "%d
    " (0212E6Ch)  
    0017741C E8 A4 9F FF FF       call        _printf (01713C5h)  
    00177421 83 C4 08             add         esp,8 

    (学习类的继承与多态后,还要学习更多相关内容。)Orz

  • 相关阅读:
    Java实现各种排序算法
    Memcached学习笔记
    S.O.L.I.D 原则
    设计模式之Bridge
    UML建模工具比较
    UML建模
    Ps经典实例教程3000例
    ps视频教程全集
    自己做到了吗?
    记事本开发Java代码注意要点
  • 原文地址:https://www.cnblogs.com/DirWang/p/12189053.html
Copyright © 2011-2022 走看看