C++多态汇编分析

C++多态细节,反汇编 反汇编——多态,面试题 阅读详情

一:说明

        C++ 中由虚函数所引起的多态总让人有一种神秘感,本文通过对汇编代码的分析使整个调用过程一目了然,但前提是读者对C++想本身有所了解,且对虚函数的调用应该有一个清晰的概念,另外读者还应有一定的汇编基础。

        文章结构:

              一:说明

              二:C++  源码

              三:分析

                  3.1:从内存中得到分析所用的一些有用的值

                  3.2:各类的成员函数(大家只需将函数名及其首地址对应起来即可)

                  3.3 函数调用分析

        建议大家先搞清楚源码,最好在自己的机器上也调试一下,然后根据程序的执行流程再去看分析。

 

      分析环境及工具:

           VC++.NET 7.1 Realease 版本;禁止优化

 

 

二:C++  源码

 

#include "stdio.h"

#include <string.h>

 

class CBase1

{

public:

    CBase1(){};

    ~CBase1(){};

    virtual void virfun1();

    virtual void virfun2();

    virtual void base1();

    int num;

    int b1num;

};

 

class CBase2

{

public:

    int num;

    CBase2(){};

    ~CBase2(){};

    virtual void virfun1();

    virtual void virfun2();

    virtual void base2();

};

 

class CSub1 : public CBase1

{

public:

    CSub1(){};

    ~CSub1(){};

    virtual void virfun1( );

};

 

class CSub2 : public  CBase1, public CBase2

{

public:

    CSub2(){};

    ~CSub2(){};

    virtual void virfun1( );

    virtual void virfun3( );

    int s2num;

};

 

class CSub3 : public CSub2

{

public:

    CSub3(){};

    ~CSub3(){};

    virtual void base2();

    virtual void virfun2( );

    virtual void virfun4( );

};

 

//------------------------------------------------------------------------------

void CBase1::virfun1()

{

    printf("run in CBase1::virfun1( ) /n");

}

 

void CBase1::virfun2()

{

    printf("run in CBase1::virfun2( ) /n");

}

 

void CBase1::base1()

{

    printf("run in CBase1::base1( ) /n");

}

 

//------------------------------------------------------------------------------

void CBase2::virfun1()

{

    printf("run in CBase2::virfun1( ) /n");

}

void CBase2::virfun2()

{

    printf("run in CBase2::virfun2( ) /n");

}

 

void CBase2::base2()

{

    printf("run in CBase2::base2( ) /n");

}

//------------------------------------------------------------------------------

void CSub1::virfun1()

{

    printf("run in CSub1::virfun1( ) /n");

}

 

void CSub2::virfun1()

{

    printf("run in CSub2::virfun1( ) /n");

}

void CSub2::virfun3()

{

    printf("run in CSub2::virfun3( ) /n");

}

 

void CSub3::virfun2()

{

    printf("run in CSub3::virfun2( ) /n");

}

void CSub3::virfun4()

{

    printf("run in CSub3::virfun4( ) /n");

}

void CSub3::base2()

{

    printf("run in CSub3::base2( ) /n");

}

 

 

CBase1 base1;

CBase2 base2;

CSub1  sub1;

CSub2  sub2;

CSub3  sub3;

 

 

CBase1  *pbase1 = 0;

CBase2  *pbase2 = 0;

CSub1  *psub1 = 0;

CSub2  *psub2 = 0;

CSub3  *psub3 = 0;

 

 

void test1()

{

    base1.virfun1();

 

    base1.num = 1;

 

    pbase1->virfun1();

 

    pbase1->num = 1;

}

 

void test2()

{

    CBase1 &tembase1 = sub1;

 

    CBase1 *ptembase1 = &sub1;

 

    sub1.virfun1();

 

    psub1->virfun1();

 

    psub1->virfun2();

 

    tembase1.virfun1();

 

    ptembase1->virfun1();

 

    sub1.virfun2();

 

    psub1->virfun2();

 

    tembase1.virfun2();

 

    ptembase1->virfun2();

 

   

    sub1.num = 1;

 

    psub1->num = 2;

   

}

 

void test3()

{

    CBase1 &tembase1 = sub2; // <<===>> CBase1 *ptembase1 = &sub2;

 

    CBase2 &tembase2 = sub2; // <<===>> CBase2 *ptembase2 = &sub2;

 

    //sub2.virfun3();

    //sub2.virfun2();    // <<===>> psub2->virfun2();

    //error C2385: 对“virfun2”(在“CSub2”中)的访问不明确

 

    psub2->virfun1();

 

    psub2->s2num = 2;

 

    tembase1.num = 3;

 

    tembase2.num = 4;

 

    psub2->virfun3();

 

    psub2->base1();

 

    psub2->base2();

 

    tembase1.virfun1();      // <<===>> ptembase1->virfun1();

 

    tembase2.virfun1();      // <<===>> ptembase2->virfun1();

   

    tembase1.virfun2();      // <<===>> ptembase1->virfun2();

 

    tembase2.virfun2();      // <<===>> ptembase2->virfun2();

}

 

void test4()

{

    CBase1 &tembase1 = sub3; // <<===>> CBase1 *ptembase1 = &sub2;

 

    CBase2 &tembase2 = sub3; // <<===>> CBase2 *ptembase2 = &sub2;

 

    CSub2 &temsub2 = sub3;

 

    psub3->virfun4();

 

    psub3->virfun2();

 

    tembase1.virfun2();

 

    tembase2.virfun2();

 

    tembase2.base2();

 

    temsub2.base2();

}

 

int main( void )

{

 

    pbase1 = &base1;

    pbase2 = &base2;

    psub1 = &sub1;

    psub2 = &sub2;

    psub3 = &sub3;

    printf("base1:%lX    base2:%lX  /nsub1:%lX       sub2:%lX      sub3:%lX/n",

       (unsigned int)pbase1, (unsigned int)pbase2, (unsigned int)psub1, (int)psub2, (int)psub3);

    printf("sizeof(...) in byte/nbase1:%lu    base2:%lu  /nsub1:%lu       sub2:%lu      sub3:%lu/n",

       sizeof(base1), sizeof(base2), sizeof(sub1), sizeof(sub2), sizeof(sub3) );

   

    test1();

    test2();

    test3();

    test4();

}

 

 

三:分析

 

3.1:从内存中得到分析所用的一些有用的值:

 

五个全局对象

    CBase1    base1;

    CBase2    base2;

    CSub1     sub1;

    CSub2     sub2;

    CSub3     sub3;

在内存中的初始分布如下:

0x0040A6F0  xx xx xx xx 14 83 40 00  sub2:40A6F4  size:24  

0x0040A6F8  00 00 00 00 00 00 00 00 

0x0040A700  08 83 40 00 00 00 00 00 

0x0040A708  00 00 00 00 f0 82 40 00  base2:40A70C size:8

0x0040A710  00 00 00 00 e4 82 40 00  base1:40A714 size:12

0x0040A718  00 00 00 00 00 00 00 00 

0x0040A720  fc 82 40 00 00 00 00 00  sub1:40A720 size:12

0x0040A728  00 00 00 00 30 83 40 00  sub3:40A72C size:24

0x0040A730  00 00 00 00 00 00 00 00 

0x0040A738  24 83 40 00 00 00 00 00 

0x0040A740  00 00 00 00

 

0x0040A6F0  xxxxxxxx 00408314  sub2:40A6F4  size:24

0x0040A6F8  00000000 00000000 

0x0040A700  00408308 00000000 

0x0040A708  00000000 004082f0  base2:40A70C size:8

0x0040A710  00000000 004082e4  base1:40A714 size:12

0x0040A718  00000000 00000000 

0x0040A720  004082fc 00000000  sub1:40A720 size:12

0x0040A728  00000000 00408330  sub3:40A72C size:24

0x0040A730  00000000 00000000 

0x0040A738  00408324 00000000 

0x0040A740  00000000

 

五个类的虚表在内存中的存储情况如下:

 

CBase1:

0x004082E4  00401000  --> CBase1::virfun1()

0x004082E8  00401020  --> CBase1::virfun2()

0x004082EC  00401040  --> CBase1::base1()

 

CBase2:

0x004082F0  00401060  --> CBase2::virfun1()

0x004082F4  00401080  --> CBase2::virfun2()

0x004082F8  004010a0  --> CBase2::base2()

 

CSub1:

0x004082FC  004010c0  --> CSub1::virfun1()

0x00408300  00401020  --> CBase1::virfun2()

0x00408304  00401040  --> CBase1::base1()

 

CSub2 偏移为 0CH 处虚表指针所指向的内容:

0x00408308  00401540  --> CSub2::virfun1`adjustor{12}

0x0040830C  00401080  --> CBase2::virfun2()

0x00408310  004010a0  --> CBase2::base2()

 

CSub2 偏移为 0 处虚表指针所指向的内容:

0x00408314  004010e0  --> CSub2::virfun1()

0x00408318  00401020  --> CBase1::virfun2()

0x0040831C  00401040  --> CBase1::base1()

0x00408320  00401100  --> CSub2::virfun3()

 

CSub3 偏移为 0CH 处虚表指针所指向的内容:

0x00408324  00401540  --> CSub2::virfun1`adjustor{12}

0x00408328  00401580  --> CSub3::virfun2`adjustor{12}

0x0040832C  00401160  --> CSub3::base2()

 

CSub3 偏移为 0 处虚表指针所指向的内容:

0x00408330  004010e0  --> CSub2::virfun1()

0x00408334  00401120  --> CSub3::virfun2()

0x00408338  00401040  --> CBase1::base1()

0x0040833C  00401100  --> CSub2::virfun3()

0x00408340  00401140  --> CBase1::base1()

0x00408344  00000000 

0x00408348  ffffffff

 

 

两个adjustor:

[thunk]:CSub2::virfun1`adjustor{12}':

00401540 83 E9 0C         sub         ecx,0Ch

00401543 E9 98 FB FF FF   jmp         CSub2::virfun1 (4010E0h)

 

[thunk]:CSub3::virfun2`adjustor{12}':

00401580 83 E9 0C         sub         ecx,0Ch

00401583 E9 98 FB FF FF   jmp         CSub3::virfun2 (401120h)

 

3.2:各类的成员函数(大家只需将函数名及其首地址对应起来即可)

 

    55: //------------------------------------------------------------------------------

    56: void CBase1::virfun1()

    57: {

00401000 55               push        ebp 

00401001 8B EC            mov         ebp,esp

00401003 51               push        ecx 

00401004 89 4D FC         mov         dword ptr [ebp-4],ecx

    58:    printf("run in CBase1::virfun1( ) /n");

00401007 68 10 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+30h (408110h)

0040100C E8 65 06 00 00   call        printf (401676h)

00401011 83 C4 04         add         esp,4

    59: }

00401014 8B E5            mov         esp,ebp

00401016 5D               pop         ebp 

00401017 C3               ret             

 

    60:

    61: void CBase1::virfun2()

    62: {

00401020 55               push        ebp 

00401021 8B EC            mov         ebp,esp

00401023 51               push        ecx 

00401024 89 4D FC         mov         dword ptr [ebp-4],ecx

    63:    printf("run in CBase1::virfun2( ) /n");

00401027 68 2C 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+4Ch (40812Ch)

0040102C E8 45 06 00 00   call        printf (401676h)

00401031 83 C4 04         add         esp,4

    64: }

00401034 8B E5            mov         esp,ebp

00401036 5D               pop         ebp 

00401037 C3               ret             

 

    65:

    66: void CBase1::base1()

    67: {

00401040 55               push        ebp 

00401041 8B EC            mov         ebp,esp

00401043 51               push        ecx 

00401044 89 4D FC         mov         dword ptr [ebp-4],ecx

    68:    printf("run in CBase1::base1( ) /n");

00401047 68 48 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+68h (408148h)

0040104C E8 25 06 00 00   call        printf (401676h)

00401051 83 C4 04         add         esp,4

    69: }

00401054 8B E5            mov         esp,ebp

00401056 5D               pop         ebp 

00401057 C3               ret             

 

    70:

    71: //------------------------------------------------------------------------------

    72: void CBase2::virfun1()

    73: {

00401060 55               push        ebp 

00401061 8B EC            mov         ebp,esp

00401063 51               push        ecx 

00401064 89 4D FC         mov         dword ptr [ebp-4],ecx

    74:    printf("run in CBase2::virfun1( ) /n");

00401067 68 64 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+84h (408164h)

0040106C E8 05 06 00 00   call        printf (401676h)

00401071 83 C4 04         add         esp,4

    75: }

00401074 8B E5            mov         esp,ebp

00401076 5D               pop         ebp 

00401077 C3               ret             

 

    76: void CBase2::virfun2()

    77: {

00401080 55               push        ebp 

00401081 8B EC            mov         ebp,esp

00401083 51               push        ecx 

00401084 89 4D FC         mov         dword ptr [ebp-4],ecx

    78:    printf("run in CBase2::virfun2( ) /n");

00401087 68 80 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+0A0h (408180h)

0040108C E8 E5 05 00 00   call        printf (401676h)

00401091 83 C4 04         add         esp,4

    79: }

00401094 8B E5            mov         esp,ebp

00401096 5D               pop         ebp 

00401097 C3               ret             

 

    80:

    81: void CBase2::base2()

    82: {

004010A0 55               push        ebp 

004010A1 8B EC            mov         ebp,esp

004010A3 51               push        ecx 

004010A4 89 4D FC         mov         dword ptr [ebp-4],ecx

    83:    printf("run in CBase2::base2( ) /n");

004010A7 68 9C 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+0BCh (40819Ch)

004010AC E8 C5 05 00 00   call        printf (401676h)

004010B1 83 C4 04         add         esp,4

    84: }

004010B4 8B E5            mov         esp,ebp

004010B6 5D               pop         ebp 

004010B7 C3               ret             

 

    85: //------------------------------------------------------------------------------

    86: void CSub1::virfun1()

    87: {

004010C0 55               push        ebp 

004010C1 8B EC            mov         ebp,esp

004010C3 51               push        ecx 

004010C4 89 4D FC         mov         dword ptr [ebp-4],ecx

    88:    printf("run in CSub1::virfun1( ) /n");

004010C7 68 B8 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+0D8h (4081B8h)

004010CC E8 A5 05 00 00   call        printf (401676h)

004010D1 83 C4 04         add         esp,4

    89: }

004010D4 8B E5            mov         esp,ebp

004010D6 5D               pop         ebp 

004010D7 C3               ret             

 

    90:

    91: void CSub2::virfun1()

    92: {

004010E0 55               push        ebp 

004010E1 8B EC            mov         ebp,esp

004010E3 51               push        ecx 

004010E4 89 4D FC         mov         dword ptr [ebp-4],ecx

    93:    printf("run in CSub2::virfun1( ) /n");

004010E7 68 D4 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+0F4h (4081D4h)

004010EC E8 85 05 00 00   call        printf (401676h)

004010F1 83 C4 04         add         esp,4

    94: }

004010F4 8B E5            mov         esp,ebp

004010F6 5D               pop         ebp 

004010F7 C3               ret             

 

    95: void CSub2::virfun3()

    96: {

00401100 55               push        ebp 

00401101 8B EC            mov         ebp,esp

00401103 51               push        ecx 

00401104 89 4D FC         mov         dword ptr [ebp-4],ecx

    97:    printf("run in CSub2::virfun3( ) /n");

00401107 68 F0 81 40 00   push        offset KERNEL32_NULL_THUNK_DATA+110h (4081F0h)

0040110C E8 65 05 00 00   call        printf (401676h)

00401111 83 C4 04         add         esp,4

    98: }

00401114 8B E5            mov         esp,ebp

00401116 5D               pop         ebp 

00401117 C3               ret             

 

    99:

   100: void CSub3::virfun2()

   101: {

00401120 55               push        ebp 

00401121 8B EC            mov         ebp,esp

00401123 51               push        ecx 

00401124 89 4D FC         mov         dword ptr [ebp-4],ecx

   102:    printf("run in CSub3::virfun2( ) /n");

00401127 68 0C 82 40 00   push        offset KERNEL32_NULL_THUNK_DATA+12Ch (40820Ch)

0040112C E8 45 05 00 00   call        printf (401676h)

00401131 83 C4 04         add         esp,4

   103: }

00401134 8B E5            mov         esp,ebp

00401136 5D               pop         ebp 

00401137 C3               ret             

 

   104: void CSub3::virfun4()

   105: {

00401140 55               push        ebp 

00401141 8B EC            mov         ebp,esp

00401143 51               push        ecx 

00401144 89 4D FC         mov         dword ptr [ebp-4],ecx

   106:    printf("run in CSub3::virfun4( ) /n");

00401147 68 28 82 40 00   push        offset KERNEL32_NULL_THUNK_DATA+148h (408228h)

0040114C E8 25 05 00 00   call        printf (401676h)

00401151 83 C4 04         add         esp,4

   107: }

00401154 8B E5            mov         esp,ebp

00401156 5D               pop         ebp 

00401157 C3               ret             

 

   108: void CSub3::base2()

   109: {

00401160 55               push        ebp 

00401161 8B EC            mov         ebp,esp

00401163 51               push        ecx 

00401164 89 4D FC         mov         dword ptr [ebp-4],ecx

   110:    printf("run in CSub3::base2( ) /n");

00401167 68 44 82 40 00   push        offset KERNEL32_NULL_THUNK_DATA+164h (408244h)

0040116C E8 05 05 00 00   call        printf (401676h)

00401171 83 C4 04         add         esp,4

   111: }

00401174 8B E5            mov         esp,ebp

00401176 5D               pop         ebp 

00401177 C3               ret             

 

3.3 函数调用分析

 

   128: void test1()

   129: {

00401180 55               push        ebp 

00401181 8B EC            mov         ebp,esp

   130:    base1.virfun1();

00401183 B9 14 A7 40 00   mov         ecx,offset base1 (40A714h)

00401188 E8 73 FE FF FF   call        CBase1::virfun1 (401000h)

       this指针存至  ECX 寄存器,对象直接调用成员函数时直接用 CALL 指令;

   131:

   132:    base1.num = 1;

0040118D C7 05 18 A7 40 00 01 00 00 00 mov         dword ptr [base1+4 (40A718h)],1

            对象直接访问简单成员变量:dword ptr [base1]  为 base1 的虚表指针,

                                      dword ptr [base1+4]为  base1 的第一个成员变量,即要访问的  base1.num

   133:

   134:    pbase1->virfun1();

00401197 A1 E0 A6 40 00   mov         eax,dword ptr [pbase1 (40A6E0h)]

            对象指针 pbase1 的内容即为所指对象的首地址,将 base1 的首地址存至 EAX 寄存器

0040119C 8B 10            mov         edx,dword ptr [eax]

            取得对象的虚表指针并存至 EDX 寄存器

0040119E 8B 0D E0 A6 40 00 mov         ecx,dword ptr [pbase1 (40A6E0h)]

            this指针存至  ECX 寄存器

004011A4 FF 12            call        dword ptr [edx]

            由虚表指针取得第一个虚函数,即所调用的virfun1()

   135:

   136:    pbase1->num = 1;

004011A6 A1 E0 A6 40 00   mov         eax,dword ptr [pbase1 (40A6E0h)]

            将 base1 的首地址存至 EAX 寄存器

004011AB C7 40 04 01 00 00 00 mov         dword ptr [eax+4],1

            访问对象的成员变量:首地址+4为成员变量的开始

   137: }

004011B2 5D               pop         ebp 

004011B3 C3               ret             

 

   138:

   139: void test2()

   140: {

004011C0 55               push        ebp 

004011C1 8B EC            mov         ebp,esp

004011C3 83 EC 08         sub         esp,8

            8字节的临时变量

   141:    CBase1 &tembase1 = sub1;

004011C6 C7 45 FC 20 A7 40 00 mov         dword ptr [tembase1],offset sub1 (40A720h)

   142:

   143:    CBase1 *ptembase1 = &sub1;

004011CD C7 45 F8 20 A7 40 00 mov         dword ptr [ptembase1],offset sub1 (40A720h)

            C++中的引用在存储处理上等价于指针,在32位机器上,每个引用各占4个字节,存储所引对象的首地址

            类  CSub1  的虚表如下:

            CSub1:

                  0x004082FC  004010c0  --> CSub1::virfun1()

                  0x00408300  00401020  --> CBase1::virfun2()

                  0x00408304  00401040  --> CBase1::base1()

   144:

   145:    sub1.virfun1();

004011D4 B9 20 A7 40 00   mov         ecx,offset sub1 (40A720h)

004011D9 E8 E2 FE FF FF   call        CSub1::virfun1 (4010C0h)

            对象直接调用函数,注意这里调用的是CSub1::virfun1(),而不是  CBase1::virfun1()

   146:

   147:    psub1->virfun1();

004011DE A1 E8 A6 40 00   mov         eax,dword ptr [psub1 (40A6E8h)]

004011E3 8B 10            mov         edx,dword ptr [eax]

004011E5 8B 0D E8 A6 40 00 mov         ecx,dword ptr [psub1 (40A6E8h)]

004011EB FF 12            call        dword ptr [edx]

            指针调用函数,分析参 test1()

   148:

   149:    psub1->virfun2();

004011ED A1 E8 A6 40 00   mov         eax,dword ptr [psub1 (40A6E8h)]

004011F2 8B 10            mov         edx,dword ptr [eax]

004011F4 8B 0D E8 A6 40 00 mov         ecx,dword ptr [psub1 (40A6E8h)]

004011FA FF 52 04         call        dword ptr [edx+4]

            指针调用函数,分析参 test1()

   150:

   151:    tembase1.virfun1();

004011FD 8B 45 FC         mov         eax,dword ptr [tembase1]

            将 sub1 的首地址存至 EAX 寄存器

00401200 8B 10            mov         edx,dword ptr [eax]

            将对象的虚表指针存至 EDX 寄存器

00401202 8B 4D FC         mov         ecx,dword ptr [tembase1]

            this指针存至  ECX 寄存器

00401205 FF 12            call        dword ptr [edx]

            引用调用函数,由生成的代码可知:C++中引用在本质上就是指针

   152:

   153:    ptembase1->virfun1();

00401207 8B 45 F8         mov         eax,dword ptr [ptembase1]

0040120A 8B 10            mov         edx,dword ptr [eax]

0040120C 8B 4D F8         mov         ecx,dword ptr [ptembase1]

0040120F FF 12            call        dword ptr [edx]

   154:

   155:    sub1.virfun2();

00401211 B9 20 A7 40 00   mov         ecx,offset sub1 (40A720h) this指针

00401216 E8 05 FE FF FF   call        CBase1::virfun2 (401020h)

   156:

   157:    psub1->virfun2();

0040121B A1 E8 A6 40 00   mov         eax,dword ptr [psub1 (40A6E8h)] 对象的首地址

00401220 8B 10            mov         edx,dword ptr [eax] 虚表指针

00401222 8B 0D E8 A6 40 00 mov         ecx,dword ptr [psub1 (40A6E8h)] this指针

00401228 FF 52 04         call        dword ptr [edx+4] 虚表中第二个函数

   158:

   159:    tembase1.virfun2();

0040122B 8B 45 FC         mov         eax,dword ptr [tembase1] 对象的首地址

0040122E 8B 10            mov         edx,dword ptr [eax] 虚表指针

00401230 8B 4D FC         mov         ecx,dword ptr [tembase1] this指针

00401233 FF 52 04         call        dword ptr [edx+4] 虚表中第二个函数

   160:

   161:    ptembase1->virfun2();

00401236 8B 45 F8         mov         eax,dword ptr [ptembase1] 对象的首地址

00401239 8B 10            mov         edx,dword ptr [eax] 虚表指针

0040123B 8B 4D F8         mov         ecx,dword ptr [ptembase1] this指针

0040123E FF 52 04         call        dword ptr [edx+4] 虚表中第二个函数

   162:

   163:   

   164:    sub1.num = 1;

00401241 C7 05 24 A7 40 00 01 00 00 00 mov         dword ptr [sub1+4 (40A724h)],1

   165:

   166:    psub1->num = 2;

0040124B A1 E8 A6 40 00   mov         eax,dword ptr [psub1 (40A6E8h)] this指针

00401250 C7 40 04 02 00 00 00 mov         dword ptr [eax+4],2

   167:   

   168: }

00401257 8B E5            mov         esp,ebp

00401259 5D               pop         ebp 

0040125A C3               ret             

 

   169:

   170: void test3()

   171: {

00401260 55               push        ebp 

00401261 8B EC            mov         ebp,esp

00401263 83 EC 0C         sub         esp,0Ch

            12字节的临时变量,其中8字节为我们声明的两个引用,另外4个字节编译器使用

   172:    CBase1 &tembase1 = sub2; // <<===>> CBase1 *ptembase1 = &sub2;

00401266 C7 45 FC F4 A6 40 00 mov         dword ptr [tembase1],offset sub2 (40A6F4h)

            类型为 CBase1 的引用 tembase1, 其内容为 sub2 的首地址

 

sub2 在内存中的初始存储情况:(从0x0040A6F4 开始,共24字节)

0x0040A6F0  xxxxxxxx 00408314

0x0040A6F8  00000000 00000000 

0x0040A700  00408308 00000000 

0x0040A708  00000000

可以看出:由于 sub2 有两个父类 CBase1 和 CBase2 (源程序中继承时这两个类的顺序对 sub2 各变量在内存中分布有很大影响,参下面分析)其中在偏移 0 处的虚表指针由 CBase1 继承而来,sub2 的非继承虚函数(CSub2 自己的虚函数)的地址会加到这个指针所指向的虚表中;在偏移 0Ch 处的虚表指针由 CBase2 继承而来,编译器会根据 CSub2 类中虚函数的实现情况修改虚表中的内容。

   173:

   174:    CBase2 &tembase2 = sub2; // <<===>> CBase2 *ptembase2 = &sub2;

0040126D B8 F4 A6 40 00   mov         eax,offset sub2 (40A6F4h)    取 sub2 的首地址

00401272 85 C0            test        eax,eax                     判断引用是否为0

00401274 74 09            je          test3+1Fh (40127Fh)         为 0 则跳转到(40127Fh) (这里不跳转)

00401276 C7 45 F4 00 A7 40 00 mov         dword ptr [ebp-0Ch],offset sub2+0Ch (40A700h)

            将对象 sub2 中偏移为 0CH 处的有效地址移到一临时变量中

0040127D EB 07            jmp         test3+26h (401286h)

0040127F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0

            如果 sub2 的首地址 0,置临时变量也为 0

00401286 8B 4D F4         mov         ecx,dword ptr [ebp-0Ch]

00401289 89 4D F8         mov         dword ptr [tembase2],ecx

            将临时变量赋值给 tembase2,即 CBase2 类型的引用

由上分析可知,CBase2 类型的引用是将 sub2 中偏移为 0CH 处的有效地址作为所“引用”对象的首地址的,这和 sub2 各变量在内存中分布相一致

   175:

   176:    //sub2.virfun3();

   177:    //sub2.virfun2();    // <<===>> psub2->virfun2();

   178:    //error C2385: 对“virfun2”(在“CSub2”中)的访问不明确

            CSub2 没有实现自己的 virfun2(),而两个父类中都有名为 virfun2 的虚函数,所以 sub2.virfun2()这种调用对函数的访问不明确

   179:

   180:    psub2->virfun1();

0040128C 8B 15 EC A6 40 00 mov         edx,dword ptr [psub2 (40A6ECh)] 取对象的首地址

00401292 8B 02            mov         eax,dword ptr [edx] 取偏移为 0 的虚表指针

00401294 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)] 取对象的首地址存到 ECX 中作为对象的this指针

0040129A FF 10            call        dword ptr [eax] 调用虚表中第一个虚函数

   181:

   182:    psub2->s2num = 2;

0040129C 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)]

004012A2 C7 41 14 02 00 00 00 mov         dword ptr [ecx+14h],2

   183:

   184:    tembase1.num = 3;

004012A9 8B 55 FC         mov         edx,dword ptr [tembase1]

004012AC C7 42 04 03 00 00 00 mov         dword ptr [edx+4],3

   185:

   186:    tembase2.num = 4;

004012B3 8B 45 F8         mov         eax,dword ptr [tembase2]

004012B6 C7 40 04 04 00 00 00 mov         dword ptr [eax+4],4

            执行完上面三条赋值语句后 sub2 在内存中的样子:

以单字节为单位:

0x0040A6F0  xx xx xx xx 14 83 40 00 

0x0040A6F8  03 00 00 00 00 00 00 00 

0x0040A700  08 83 40 00 04 00 00 00 

0x0040A708  02 00 00 00

以四字节为单位:

0x0040A6F0  xxxxxxxx 00408314 

0x0040A6F8  00000003 00000000 

0x0040A700  00408308 00000004 

0x0040A708  00000002

由此可对 sub2 中各变量在内存中的分布有更进一步的认识

   187:

   188:    psub2->virfun3();

004012BD 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)]

004012C3 8B 11            mov         edx,dword ptr [ecx]

004012C5 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)]

004012CB FF 52 0C         call        dword ptr [edx+0Ch]

   189:

   190:    psub2->base1();

004012CE A1 EC A6 40 00   mov         eax,dword ptr [psub2 (40A6ECh)]

004012D3 8B 10            mov         edx,dword ptr [eax]

004012D5 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)]

004012DB FF 52 08         call        dword ptr [edx+8]

   191:

   192:    psub2->base2();

004012DE 8B 0D EC A6 40 00 mov         ecx,dword ptr [psub2 (40A6ECh)] 取 sub2 首地址于 ECX 中

004012E4 83 C1 0C         add         ecx,0Ch 将 sub2 中偏移为 0CH 的有效地址作为调用 base2()时所用的this指针

004012E7 A1 EC A6 40 00   mov         eax,dword ptr [psub2 (40A6ECh)] 取 sub2 首地址

004012EC 8B 50 0C         mov         edx,dword ptr [eax+0Ch] 取偏移为 0CH 的虚表指针

004012EF FF 52 08         call        dword ptr [edx+8] 调用虚表中第三个虚函数

            注意,在调用 CBase1::base1() 和调用 CBase2::base2()函数时函数所使用的 this 指针,也就是 ECX 寄存器的内容是不一样的,请注意区别

   193:

            在 C++ 中,只有指针或引用支持 OO 程序设计中所需的多态性质。下面两个调用在代码上看似相同,执行结果也相同:都是调用 CSub2::virfun1(),但应该注意到引用 tembase1 与 tembase2 是不一样的:引用 tembase1 内容为 sub2 的首地址,引用 tembase2 内容为 sub2 中偏移为 0CH 处的地址。下面具体分析:

   194:    tembase1.virfun1();      // <<===>> ptembase1->virfun1();

004012F2 8B 45 FC         mov         eax,dword ptr [tembase1]

            sub2 的首地址移至 EAX

004012F5 8B 10            mov         edx,dword ptr [eax]

            sub2 中偏移为 0 的虚表指针移至 EDX

004012F7 8B 4D FC         mov         ecx,dword ptr [tembase1]

            sub2 的首地址移至 ECX 作为调用函数所用的 this 指针

004012FA FF 12            call        dword ptr [edx]

            调用虚表中第一个函数,即 CSub2::virfun1()

 

CSub2 偏移为 0 处虚表指针所指向的内容:

0x00408314  004010e0  --> CSub2::virfun1()

0x00408318  00401020  --> CBase1::virfun2()

0x0040831C  00401040  --> CBase1::base1()

0x00408320  00401100  --> CSub2::virfun3()

 

   195:

   196:    tembase2.virfun1();      // <<===>> ptembase2->virfun1();

004012FC 8B 45 F8         mov         eax,dword ptr [tembase2]

            sub2 中偏移为 0CH 处的地址移至 EAX

004012FF 8B 10            mov         edx,dword ptr [eax]

            sub2 中偏移为 0CH 的虚表指针移至 EDX

00401301 8B 4D F8         mov         ecx,dword ptr [tembase2]

            sub2 的偏移为 0CH 处的地址移至 ECX 作为调用函数所用的 this 指针吗?

00401304 FF 12            call        dword ptr [edx]

            调用偏移为 0CH 的虚表中第一个函数,但目前看函数所需的 this 指针不对呀:实际执行的是 CSub2::virfun1(),那么在执行前 ECX 的值应为 sub2 的首地址才对。让我们再看一下虚表中的内容吧:

 

CSub2 偏移为 0CH 处虚表指针所指向的内容:

0x00408308  00401540  --> CSub2::virfun1`adjustor{12}

0x0040830C  00401080  --> CBase2::virfun2()

0x00408310  004010a0  --> CBase2::base2()

这个虚表中第一个项不是 CSub2::virfun1() 的地址,而是一个所谓的 adjustor 的地址,让我们再看看这个 adjustor:

 

[thunk]:CSub2::virfun1`adjustor{12}':

00401540 83 E9 0C         sub         ecx,0Ch

00401543 E9 98 FB FF FF   jmp         CSub2::virfun1 (4010E0h)

到此,一切都清楚了:在  adjustor 中, ECX 的值终于改成了我们所认为的 sub2 的首地址,而且最终执行的是 CSub2::virfun1()。下面两个函数的调用和上面类似,只不过 CSub2 偏移为 0CH 处虚表指针所指向的虚表中第二项不是adjustor 的地址,而是 CBase2::virfun2() 的地址,那么在调用tembase2.virfun2()时 ECX 的值就是所要的:sub2 的偏移为 0CH 处的地址

   197:   

   198:    tembase1.virfun2();      // <<===>> ptembase1->virfun2();

00401306 8B 45 FC         mov         eax,dword ptr [tembase1]

00401309 8B 10            mov         edx,dword ptr [eax]

0040130B 8B 4D FC         mov         ecx,dword ptr [tembase1]

0040130E FF 52 04         call        dword ptr [edx+4]

   199:

   200:    tembase2.virfun2();      // <<===>> ptembase2->virfun2();

00401311 8B 45 F8         mov         eax,dword ptr [tembase2]

00401314 8B 10            mov         edx,dword ptr [eax]

00401316 8B 4D F8         mov         ecx,dword ptr [tembase2]

00401319 FF 52 04         call        dword ptr [edx+4]

   201: }

0040131C 8B E5            mov         esp,ebp

0040131E 5D               pop         ebp 

0040131F C3               ret             

   202:

 

test4()中各调用和上面分析无本质差别,有兴趣的朋友可自己分析一下。

 

   203: void test4()

   204: {

00401320 55               push        ebp 

00401321 8B EC            mov         ebp,esp

00401323 83 EC 10         sub         esp,10h

   205:    CBase1 &tembase1 = sub3; // <<===>> CBase1 *ptembase1 = &sub2;

00401326 C7 45 FC 2C A7 40 00 mov         dword ptr [tembase1],offset sub3 (40A72Ch)

   206:

   207:    CBase2 &tembase2 = sub3; // <<===>> CBase2 *ptembase2 = &sub2;

0040132D B8 2C A7 40 00   mov         eax,offset sub3 (40A72Ch)

00401332 85 C0            test        eax,eax

00401334 74 09            je          test4+1Fh (40133Fh)

00401336 C7 45 F0 38 A7 40 00 mov         dword ptr [ebp-10h],offset sub3+0Ch (40A738h)

0040133D EB 07            jmp         test4+26h (401346h)

0040133F C7 45 F0 00 00 00 00 mov         dword ptr [ebp-10h],0

00401346 8B 4D F0         mov         ecx,dword ptr [ebp-10h]

00401349 89 4D F8         mov         dword ptr [tembase2],ecx

   208:

   209:    CSub2 &temsub2 = sub3;

0040134C C7 45 F4 2C A7 40 00 mov         dword ptr [temsub2],offset sub3 (40A72Ch)

   210:

   211:    psub3->virfun4();

00401353 8B 15 F0 A6 40 00 mov         edx,dword ptr [psub3 (40A6F0h)]

00401359 8B 02            mov         eax,dword ptr [edx]

0040135B 8B 0D F0 A6 40 00 mov         ecx,dword ptr [psub3 (40A6F0h)]

00401361 FF 50 10         call        dword ptr [eax+10h]

   212:

   213:    psub3->virfun2();

00401364 8B 0D F0 A6 40 00 mov         ecx,dword ptr [psub3 (40A6F0h)]

0040136A 8B 11            mov         edx,dword ptr [ecx]

0040136C 8B 0D F0 A6 40 00 mov         ecx,dword ptr [psub3 (40A6F0h)]

00401372 FF 52 04         call        dword ptr [edx+4]

   214:

   215:    tembase1.virfun2();

00401375 8B 45 FC         mov         eax,dword ptr [tembase1]

00401378 8B 10            mov         edx,dword ptr [eax]

0040137A 8B 4D FC         mov         ecx,dword ptr [tembase1]

0040137D FF 52 04         call        dword ptr [edx+4]

   216:

   217:    tembase2.virfun2();

00401380 8B 45 F8         mov         eax,dword ptr [tembase2]

00401383 8B 10            mov         edx,dword ptr [eax]

00401385 8B 4D F8         mov         ecx,dword ptr [tembase2]

00401388 FF 52 04         call        dword ptr [edx+4]

   218:

   219:    tembase2.base2();

0040138B 8B 45 F8         mov         eax,dword ptr [tembase2]

0040138E 8B 10            mov         edx,dword ptr [eax]

00401390 8B 4D F8         mov         ecx,dword ptr [tembase2]

00401393 FF 52 08         call        dword ptr [edx+8]

   220:

   221:    temsub2.base2();

00401396 8B 4D F4         mov         ecx,dword ptr [temsub2]

00401399 83 C1 0C         add         ecx,0Ch

0040139C 8B 45 F4         mov         eax,dword ptr [temsub2]

0040139F 8B 50 0C         mov         edx,dword ptr [eax+0Ch]

004013A2 FF 52 08         call        dword ptr [edx+8]

   222: }

004013A5 8B E5            mov         esp,ebp

004013A7 5D               pop         ebp 

004013A8 C3               ret        

 

链接:http://blog.youkuaiyun.com/sandrain_zeq/archive/2006/06/20/815880.aspx

汇编分析C++class 汇编层面的多态如何实现,this指针,new和malloc差异分析 阅读详情

相关推荐

C++虚函数实现多态的机制

虚函数是C++实现多态的机制,那么它是如何做到的呢? 以下通过反汇编探索虚函数内存模型,查看虚函数实现多态的过程。 工具 Visual studio 2017:以下程序仅做VC++编译器下的32位程序探讨,其他编译器与64位程序所产生的差异不作讨论。 反汇编过程 首先声明一个不包含虚函数的简单C++类,如下: 在构造函数中加入断点,使得反汇编构造函数代码,如图: 当运行到断点时,在Visual...

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1063

从底层汇编理解C++多态原理

这门课的初衷就是让大家能够从汇编的角度看多态实现的背后原理,在理解原理的基础上我们自己动手实践。在面试(多态是面试题的高频题目)的过程中,能够做到心中自有丘壑。自己动手模拟多态的原理过程中,去如何实现虚表,继承的结构。

半导体关键系统之RMS浅谈

这种系统还要求设备在设备上的配方发生变化时通知工厂主机,并需要与EAP(Equipment Automation Process)联结,旨在实时管控自动化系统所用配方与用户当初的设定是否匹配,从而使得自动化系统所用的配方更加趋于安全。例如,制药行业中的药品生产管理系统(GMP),它对药品的生产过程进行严格控制,包括原材料的采购、生产工艺流程、质量控制等方面。这些系统通常与企业的生产系统集成,能够对产品的配方进行精细化管理,包括配方的设计、试验、验证、生产等环节。

阿拉伯梳子的专栏 6160

【硬件深似海】三极管

三极管中两个结,集电结和发射结,vbb使发射结耗尽层变窄开启,n区(发射区)的自由电子进入p区(基区),一小部分形成ib另一部分跨越集电结形成ic,当vbb增大更多的自由电子进入基区,但是由于ib很小即便vbb增大增大的也不多,所以ic也增大由上述可以看出vbb翻倍ib翻倍,发射区进入基区的自由电子也翻倍,ic也翻倍,所以ib和ic有一个线性关系β,如果vbb太小的话由于集电结反偏的耗尽层形成一个反向电场使得vcc不能控制进入基区的自由电子数目,但是vbb可以控制由发射区进入基区的自由电子数目也就是书上的i

weixin_43323830的博客 458

如何用 Python 实现自动化测试?详细步骤和代码示例

Python自动化测试实战指南 本文系统介绍了使用Python构建自动化测试体系的完整方案。主要内容包括: 环境搭建:Python 3.8+虚拟环境配置,安装pytest、Selenium等核心测试库 分层测试实现: 单元测试:通过计算器示例演示pytest断言和异常测试 API测试:使用requests库进行RESTful接口验证 UI测试:Selenium WebDriver实现网页交互测试 高级特性: 使用fixture管理测试依赖 参数化实现数据驱动测试 生成HTML和Allure测试报告 GitH

m0_64424835的博客 452

SAP STO流程实战:如何配置外向交货单PGI后自动生成内向交货单(附避坑指南)

本文详细解析了SAP STO流程中,如何配置实现外向交货单发货过账(PGI)后自动生成内向交货单。通过分步指南,重点阐述了SPED输出类型、输出条件记录、收货点及确认控制码等关键配置,并提供了针对“No goods receipt possible”等常见错误的避坑指南,助力实现跨公司物料转移流程的自动化与高效衔接。

weixin_28327051的博客 395

门面模式Facade

门面模式是结构型模式的一种,它将多个分散的类所做的工作集中到一个类中完成,在高层给出来统一的接口。 下面给出UML类图和C++示例代码: #ifndef __FACADE_H__ #define __FACADE_H__ class CSubSystem1 { public: CSubSystem1() {} ~CSubSystem1() {} publi

HW的博客 864

嘉立创EDA实战:从点亮第一盏灯到完成首块PCB

本文详细介绍了使用嘉立创EDA从点亮第一盏LED灯到完成首块PCB的全过程。通过实战案例分享,包括原理图绘制、PCB设计、DRC检查、覆铜操作等关键步骤,帮助电子设计爱好者快速掌握嘉立创EDA的使用技巧,并避免常见的设计错误。

weixin_30314793的博客 373

父类指针、子类指针,多态,虚表

目录 父类指针指向子类对象 子类指针指向父类对象 多态 c++默认根据指针类型调用对应的函数 非多态demo c++中的多态通过虚函数实现(virtual修饰) 虚函数实现demo 虚函数实现原理 汇编分析 内存分析 父类指针指向子类对象 父类指针指向子类对象,是安全的的(继承方式必须是public) 子类指针指向父类对象 子类指针指向父类对象是不安全的 struct Person { int m_age; }; struct Student...

huanhuaqian的博客 1342

手把手教你用STM32的SPI驱动MCP2517FD实现CAN FD通信(附完整代码)

本文详细介绍了如何使用STM32通过SPI驱动MCP2517FD实现CAN FD通信,涵盖硬件连接、SPI驱动编写、寄存器配置及数据收发实战。提供完整代码示例和常见问题排查指南,帮助开发者快速构建高可靠性的CAN FD通信系统,适用于汽车电子和工业控制等领域。

weixin_30607029的博客 676

高速电路设计必看:同步开关噪声问题解析

数字电路中的同步开关噪声(SSN)问题日益突出,尤其是随着高速器件应用增多。本文分析了地弹和电压弹的产生机理,指出其与电源网络电感、输出驱动电流变化率密切相关。文章详细阐述了地弹参数特征,包括峰值电压、脉冲宽度等关键指标,并探讨了负载电容、信号压摆率等因素对地弹的影响。最后提出系统化的解决方案,涵盖芯片选型、电路设计、PCB布局和后期调试等多个环节,强调通过降低回路电感和控制电流变化率来有效抑制SSN。这些措施需要在性能、成本和复杂度之间取得平衡,为高速数字电路设计提供了重要参考。

一只豌豆象的博客 299

C++之this指针与另一种“多态”

C++之this指针与另一种“多态” 2010-11-09 9:47 一、 引入   定义一个类的对象,首先系统已经给这个对象分配了空间,然后会调用构造函数(说明:假设存在构造函数--2010.9.5修正)。一个类有多个对象,当程序中调用对象的某个函数时,有可能要访问到这个对象的成员变量。而对于同一个类的每一个对象,都是共享同一份类函数。对象有单独的变量,但是没有单独的函数,所以当调用函

lionzl的专栏 643

如何让 Mac mini 24小时不休眠运行 OpenClaw

如果 OpenClaw 每 5-10 分钟自动退出,launchd 连续 4 次快速重启后会判定为崩溃循环并卸载服务。:方案一(pmset)+ 方案三(launchd)组合使用,既防止系统休眠,又保证 OpenClaw 开机自启和崩溃自动恢复。:caffeinate 只在进程运行期间有效,终端关闭或进程结束后系统恢复正常睡眠策略。这条命令会自动创建 launchd plist 并启用服务,是最推荐的方式。替换为你的实际用户名路径,Node.js 路径也要改成你自己的。确保 plist 中有。

Maynor的博客 2214

C++逆向分析--虚函数(多态的前置)

真正调用的其实是call eax这句汇编代码,我们看到他的硬编码是FF D0。说明他是个间接调用。将this指针的第一项放入eax,又将eax的第一项复制给edx,此时将this指针给ecx(如果是linux平台g++编译this指针是赋值给rdi的 _x64情况下)最后将edx的第一项赋值给eax。我们先来看一个例子,这次试验用的是windows平台下的VS编译器不同编译器的细节是不一样的原理大差不差。现在我们知道虚函数,如果是通过对象去调用,和普通成员方法没什么区别,但是如果通过指针或者引用去调用。

qq_30528603的博客 2685

vue3中使用antv-S2表格(基础功能版)

表格的数值默认会渲染排序 icon, 可以配置 showDefaultHeaderActionIcon关闭。当配置为对象时,always 控制是否在子维度不足 2 个时始终展示小计,默认不展示。首先创建一个vue3+vite+ts项目,可以查看下面相关文章,只看第一条即可。可以调整行宽、列宽、列排序、单个单元格详情展示、选中某列、某行等基本功能。可以通过上面设置的SUM函数自动计算出数值,也可以自定义累加函数。配置自定义方法优先级大于配置聚合方式,即配置。1、在项目中安装(npm/pnpm都可以)

蜗牛~~ 2210

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

深入剖析C++实现多态的原理

文章目录前言有虚函数的类对象模型派生类继承有虚函数的基类的对象模型无虚函数派生类继承有虚函数基类的对象模型有虚函数派生类继承有虚函数的基类对象模型多态原理剖析汇编代码分析多态的过程有关多态的常见几个问题 前言 要理解C++的多态,必须先学会使用多态: 多态的基本使用,我总结一篇文章:有需要可以看看 【传送门】:谈谈C++多态的基本使用和总结 有虚函数的类对象模型 class Base { public: Base(){ cout << "Base::Base()的构造函数调用" &lt

呋喃吖的博客 1795

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

深入理解c++多态实现原理

引言       多态是指通过基类的指针或者引用,在运行时动态调用实际绑定对象函数的行为。与之相对应的编译时绑定函数称为静态绑定。多态是面向对象编程的核心思想之一,因此我们有必要深入探索一下它的实现原理。理解了原理才能更好的使用。前置条件        现有代码如下所示,非常简单的例子。通过基类的引用调用recv函数来触发多态。接下来的分析涉及汇编知识,如果还没熟悉汇编,可以看另外一篇文章&lt;...

kwanson的博客 1万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【C++】多态

虚函数重写,多态调用,抽象类和接口继承,多态原理,常见面试题…

The porters of knowledge 1075

深入理解c++多态的实现原理

允许不同的对象,对相同的消息产生不同的响应;c++中多态分为两种:一种是静态多态,主要指的是函数重载;另一种是动态多态,通过函数覆盖(重写)实现;本文主要讨论动态多态,以下统称为多态。使用基类指针或引用指向子类对象,且调用虚函数时,就会根据指针或引用指向的对象的实际类型,访问实际对象重写的虚函数。使用基类指针或引用指向子类对象,且调用虚函数时,就会发生多态;多态的实现依赖于虚函数,虚表指针和虚函数表;

qq_54199569的博客 836

C语言汇编实现分析

C语言汇编实现原理分析

qq_25305565的博客 806

C++:多态(底层原理介绍)

C++多态原理介绍

weixin_57663528的博客 1509

关于C++ 多态实现技术的深度解析(vfptr,vftable)

#PS:要转载请注明出处,本人版权所有 #PS:这个只是 《 我自己 》理解,如果和你的 #原则相冲突,请谅解,勿喷 前置说明: 关于C++的多态,我就不多说了,我理解的一句话就是:一个接口,多种实现(简称:一对多)。在C++里面,多态的实现方法就是继承,简单来说:就是基类提供接口,子类可以实现基类的接口,不同的子类就构成了基类接口的不同的实现。以上就是C++课本上或者C++...

Sky的专栏 5341

C++ | 动多态的发生时机

探究动多态的发生时机 有了虚函数和虚函数表为动多态提供支持,从而可以实现C++语言的动多态。那么,问题又来了。 动多态的发生时机是什么? 或者说,动多态发生有哪些条件与限制呢? 下面让我们一起来探究动多态的秘密,揭示动多态的发生时机。 详细步骤: 1、虚函数与普通函数的调用 2、利用汇编代码分析动多态 3、初步探究动多态调用方式 4、深入探究动多态发生时机 5、总结 1、虚函数与普通函数的...

专注于C/C++/Linux领域创作 468

Win32汇编:仿写多态与虚函数

多态性是面向对象的重要组成部分,利用多态可以设计和实现易于扩展的程序,所谓多态就是一个类函数有多重形态,具有不同功能的函数可以用同一个函数名,实现使用一个函数名调用不同内容的函数,从而返回不同的结果,这就是多态性,多态离不开虚函数的支撑,以下案例本人将深度分析虚函数实现机制,并通过汇编实现虚函数机制。

微软技术分享 7982
上一篇: 函数调用规范__cdecl和__stdcall的区别
下一篇: 关于IC研发转销售的职业咨询
jiangdf
博客等级 码龄19年 86粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值