C++中堆对象的构造函数和析构函数逆向分析

  • 实验环境:
    • 操作系统:Windows XP Professional Service Pack 3
    • 集成开发环境:Microsoft Visual C++ 6.0
    • 构建版本:Debug版本
  • 实验代码:
  •  1 #include <stdio.h>
     2
     3 class People
     4 {
     5 public:
     6     People() { printf("Constructor!\n"); }
     7     ~People() { printf("Destructor!\n"); }
     8 };
     9
    10 int main(int argc, char **argv, const char **envp)
    11 {
    12     People *p = new People;
    13     delete p;
    14
    15     return 0;
    16 } 
  • 下面先来看在堆上构造一个People对象的汇编代码,这里有一个需要注意的是,虽然People类的定义中没有定义数据成员,表象上占用空间为0,但是实际上需要分配1个字节的空间,否则就无法获取对象实例的地址了,this指针也会失效,导致不能被实例化,所以在调用new函数的时候push了1个字节,用来表征分配1个字节的堆空间。
  • 12:       People *p = new People;
               ; 压入待分配的类对象所需要的空间  
    0040105D   push        1
               ; 调用new函数进行分配堆空间
    0040105F   call        operator new (004012b0)
               ; 由于new函数是__cdecl调用类型,所以有调用者负责恢复堆栈平衡
    00401064   add         esp,4
               ; 将分配的堆地址存放到临时变量[ebp - 18h]中
    00401067   mov         dword ptr [ebp-18h],eax
               ; [ebp - 4]保存申请堆空间的次数
    0040106A   mov         dword ptr [ebp-4],0
               ; 检测堆空间是否申请成功
    00401071   cmp         dword ptr [ebp-18h],0
               ; 如果申请失败的话,就跳过构造函数,跳转到0x401084将存放this指针的临时变量赋值为0
    00401075   je          main+54h (00401084)
               ; 将分配的堆空间的首地址传入ecx中
    00401077   mov         ecx,dword ptr [ebp-18h]
               ; 调用构造函数
    0040107A   call        @ILT+15(People::People) (00401014)
               ; 构造函数调用返回this指针,将this指针传入eax中,此处又将this指针传入临时变量[ebp - 24h]中
    0040107F   mov         dword ptr [ebp-24h],eax
               ; 跳过new操作分配堆空间失败的赋值操作
    00401082   jmp         main+5Bh (0040108b)
               ; 如果分配失败的话,就会把临时变量[ebp - 24h]赋值为0
    00401084   mov         dword ptr [ebp-24h],0
               ; 将[ebp - 24h]重新传入eax中,如果为0,则代表堆空间分配失败,否则分配成功
    0040108B   mov         eax,dword ptr [ebp-24h]
               ; 下面几步操作最终将this指针保存到了变量[ebp - 14h]中,也就是我们的变量p
    0040108E   mov         dword ptr [ebp-14h],eax
    00401091   mov         dword ptr [ebp-4],0FFFFFFFFh
    00401098   mov         ecx,dword ptr [ebp-14h]
    0040109B   mov         dword ptr [ebp-10h],ecx
  • 接下来再看一下单个对象析构函数的调用的汇编代码,这里有一个需要注意的是,delete一个堆对象的时候不是简单的调用析构函数,而是调用了一个析构代理函数,原因有很多,其中一个原因是有时候并不仅仅只销毁一个对象,还有可能使用delete[]销毁多个对象。
  • 13:       delete p;
               ; 下面几步操作将变量p的内容传入了临时变量[ebp - 1Ch]中
    0040109E   mov         edx,dword ptr [ebp-10h]
    004010A1   mov         dword ptr [ebp-20h],edx
    004010A4   mov         eax,dword ptr [ebp-20h]
    004010A7   mov         dword ptr [ebp-1Ch],eax
               ; 判断变量p的内容是否为0
    004010AA   cmp         dword ptr [ebp-1Ch],0
               ; 如果为0, 则直接跳过析构函数的调用
    004010AE   je          main+8Fh (004010bf)
               ; 压入释放对象类型标志, 1为单个对象, 3为释放对象数组, 0表示仅仅执行析构函数, 不释放堆空间(与多重继承有关)
    004010B0   push        1
               ; 将对象指针传入ecx中
    004010B2   mov         ecx,dword ptr [ebp-1Ch]
               ; 这里不是直接调用析构函数, 而是调用了一个叫做‘scalar deleting destructor‘的析构代理函数, 下面将要介绍
    004010B5   call        @ILT+0(People::`scalar deleting destructor‘) (00401005)
               ; 将被销毁的对象的首地址传入临时变量[ebp - 28h]中
    004010BA   mov         dword ptr [ebp-28h],eax
               ; 处理成功, 跳过失败处理
    004010BD   jmp         main+96h (004010c6)
               ; 如果析构失败, 则将临时变量[ebp - 28h]赋值为0
    004010BF   mov         dword ptr [ebp-28h],0 
  • 跟进析构代理函数看看,看上去好像也仅仅是在delete函数的基础上加了调用析构函数,不过析构代理函数在析构多个对象的时候会有很大的不同。
  • People::`scalar deleting destructor‘:
               ; 下面几步操作属于函数的入口操作, 和我们的分析无关, 忽略
    00401160   push        ebp
    00401161   mov         ebp,esp
    00401163   sub         esp,44h
    00401166   push        ebx
    00401167   push        esi
    00401168   push        edi
    00401169   push        ecx
    0040116A   lea         edi,[ebp-44h]
    0040116D   mov         ecx,11h
    00401172   mov         eax,0CCCCCCCCh
    00401177   rep stos    dword ptr [edi]
               ; 将this指针从栈中弹到ecx中
    00401179   pop         ecx
               ; 将this指针存放到临时变量[ebp - 4]中
    0040117A   mov         dword ptr [ebp-4],ecx
               ; 将this指针又赋值给了ecx, 重复操作
    0040117D   mov         ecx,dword ptr [ebp-4]
               ; 调用析构函数, 这里使用寄存器ecx进行传递this指针
    00401180   call        @ILT+10(People::~People) (0040100f)
               ; 获取释放标志
    00401185   mov         eax,dword ptr [ebp+8]           ; 检查是否释放堆空间
    00401188   and         eax,1
    0040118B   test        eax,eax
    0040118D   je          People::`scalar deleting destructor‘+3Bh (0040119b)           ; ecx保存了对象的首地址
    0040118F   mov         ecx,dword ptr [ebp-4]
               ; 压入this指针
    00401192   push        ecx
               ; 调用delete函数
    00401193   call        operator delete (00401220)
               ; 由于delete函数也是__cdecl调用类型, 所以也是由调用者进行堆栈平衡操作
    00401198   add         esp,4
               ; 将已经被销毁的对象的首地址传入eax中
    0040119B   mov         eax,dword ptr [ebp-4]
               ; 函数离开的收尾工作, 和我们的分析无关, 忽略
    0040119E   pop         edi
    0040119F   pop         esi
    004011A0   pop         ebx
    004011A1   add         esp,44h
    004011A4   cmp         ebp,esp
    004011A6   call        __chkesp (004016b0)
    004011AB   mov         esp,ebp
    004011AD   pop         ebp
    004011AE   ret         4 
  • 至于析构函数本身,倒是没有什么需要特别注意的点。
  • 7:        ~People() { printf("Destructor!\n"); }
               ; 函数入口操作, 忽略
    004011D0   push        ebp
    004011D1   mov         ebp,esp
    004011D3   sub         esp,44h
    004011D6   push        ebx
    004011D7   push        esi
    004011D8   push        edi
    004011D9   push        ecx
    004011DA   lea         edi,[ebp-44h]
    004011DD   mov         ecx,11h
    004011E2   mov         eax,0CCCCCCCCh
    004011E7   rep stos    dword ptr [edi]
               ; 将this指针存放到ecx寄存器中
    004011E9   pop         ecx
               ; 将this指针传入临时变量[ebp - 4]
    004011EA   mov         dword ptr [ebp-4],ecx
               ; 压入格式化字符串
    004011ED   push        offset string "Destructor!\n" (0042502c)
               ; 调用printf函数
    004011F2   call        printf (004017e0)
               ; 由于不定参数, 所以printf必须得是__cdecl调用类型, 由调用者进行堆栈平衡
    004011F7   add         esp,4
               ; 函数收尾工作, 忽略
    004011FA   pop         edi
    004011FB   pop         esi
    004011FC   pop         ebx
    004011FD   add         esp,44h
    00401200   cmp         ebp,esp
    00401202   call        __chkesp (004016b0)
    00401207   mov         esp,ebp
    00401209   pop         ebp
    0040120A   ret 
  • 下面在看看多个对象的构造和析构情况,
  • 实验代码:
  •  1 #include <stdio.h>
     2
     3 class People
     4 {
     5 public:
     6     People() {}
     7     ~People() {}
     8 };
     9
    10 int main(int argc, char **argv, const char **envp)
    11 {
    12     People *p = new People[5];
    13     delete[] p;
    14
    15     return 0;
    16 } 
  • 下面看看分配多个对象的汇编代码,这里调用了一个构造代理函数。
  • 12:       People *p = new People[5];
               ; 每个待分配的对象占用1个字节, 再加上堆空间的首地址处的4字节用于保存对象的个数
    0040105D   push        9
               ; 调用new函数
    0040105F   call        operator new (004012b0)
               ; 调用方负责堆栈平衡
    00401064   add         esp,4
               ; eax保存分配的堆空间的首地址
    00401067   mov         dword ptr [ebp-18h],eax
               ; [ebp - 4]保存申请堆空间的次数
    0040106A   mov         dword ptr [ebp-4],0
               ; 判断是否分配成功, 如果分配失败的话就跳过构造代理函数
    00401071   cmp         dword ptr [ebp-18h],0
    00401075   je          main+75h (004010a5)
               ; 压入析构函数的地址
    00401077   push        offset @ILT+10(People::~People) (0040100f)
               ; 压入构造函数的地址
    0040107C   push        offset @ILT+15(People::People) (00401014)
               ; 将分配的堆空间的首地址传入eax中
    00401081   mov         eax,dword ptr [ebp-18h]
               ; 堆空间的首地址处的4字节用于存放申请的对象的个数
    00401084   mov         dword ptr [eax],5
               ; 压入对象个数
    0040108A   push        5
               ; 压入每个对象的大小
    0040108C   push        1
               ; 将堆空间的首地址传入ecx中
    0040108E   mov         ecx,dword ptr [ebp-18h]
               ; 跳过首地址处的4个用于存放大小的字节
    00401091   add         ecx,4
               ; 压入ecx
    00401094   push        ecx
               ; 调用构造代理函数
    00401095   call        `eh vector constructor iterator‘ (0040ee90)
               ; 将首地址传入edx中
    0040109A   mov         edx,dword ptr [ebp-18h]
               ; 跳过首地址处的4个用于存放大小的字节
    0040109D   add         edx,4
               ; 将edx传入[ebp - 24h]中
    004010A0   mov         dword ptr [ebp-24h],edx
               ; 跳过分配失败处理
    004010A3   jmp         main+7Ch (004010ac)
               ; 分配失败处理
    004010A5   mov         dword ptr [ebp-24h],0
               ; 最终将对象首地址存入[ebp - 10h]中
    004010AC   mov         eax,dword ptr [ebp-24h]
    004010AF   mov         dword ptr [ebp-14h],eax
    004010B2   mov         dword ptr [ebp-4],0FFFFFFFFh
    004010B9   mov         ecx,dword ptr [ebp-14h]
    004010BC   mov         dword ptr [ebp-10h],ecx
  • 跟进构造代理函数看看
  • ??[email protected]@[email protected]:
               ; 以下代码是函数入口的初始化和异常链的处理
    0040EE90   push        ebp
    0040EE91   mov         ebp,esp
    0040EE93   push        0FFh
    0040EE95   push        offset string "stream != NULL"+30h (00426068)
    0040EE9A   push        offset __except_handler3 (00407590)
    0040EE9F   mov         eax,fs:[00000000]
    0040EEA5   push        eax
    0040EEA6   mov         dword ptr fs:[0],esp
    0040EEAD   add         esp,0F0h
    0040EEB0   push        ebx
    0040EEB1   push        esi
    0040EEB2   push        edi
    0040EEB3   mov         dword ptr [ebp-20h],0
    0040EEBA   mov         dword ptr [ebp-4],0
               ; 通过对下面代码的分析可以知道, [ebp - 1Ch]存放的是循环次数
    0040EEC1   mov         dword ptr [ebp-1Ch],0
    0040EEC8   jmp         `eh vector constructor iterator‘+43h (0040eed3)
               ; 将循环次数存入eax
    0040EECA   mov         eax,dword ptr [ebp-1Ch]
               ; 将循环次数加1
    0040EECD   add         eax,1
               ; 重新存回[ebp - 1Ch]中
    0040EED0   mov         dword ptr [ebp-1Ch],eax
               ; 循环代码开始
               ; 将循环次数传入ecx
    0040EED3   mov         ecx,dword ptr [ebp-1Ch]
               ; 与对象个数进行比较, 即进行ecx - dword ptr [ebp + 10h]操作
    0040EED6   cmp         ecx,dword ptr [ebp+10h]
               ; 如果大于等于0, 就离开循环代码块
    0040EED9   jge         `eh vector constructor iterator‘+5Ch (0040eeec)
               ; 将对象的首地址(已经跳过用于存放大小的4个字节)传入
    0040EEDB   mov         ecx,dword ptr [ebp+8]
               ; 调用构造函数
    0040EEDE   call        dword ptr [ebp+14h]
               ; 将对象的首地址传入eax
    0040EEE1   mov         edx,dword ptr [ebp+8]
               ; 在上一个对象的首地址的基础上加上对象的大小(此处为1个字节)
    0040EEE4   add         edx,dword ptr [ebp+0Ch]
               ; 将新的待调用构造函数的对象首地址存回[ebp + 8]中
    0040EEE7   mov         dword ptr [ebp+8],edx
               ; 跳转回循环块首
    0040EEEA   jmp         `eh vector constructor iterator‘+3Ah (0040eeca)
               ; 剩余代码与我们的分析无关, 忽略
    0040EEEC   mov         dword ptr [ebp-20h],1
    0040EEF3   mov         dword ptr [ebp-4],0FFFFFFFFh
    0040EEFA   call        `eh vector constructor iterator‘+71h (0040ef01)
    0040EEFF   jmp         `eh vector constructor iterator‘+8Dh (0040ef1d)
    0040EF01   cmp         dword ptr [ebp-20h],0
    0040EF05   jne         `eh vector constructor iterator‘+8Ch (0040ef1c)
    0040EF07   mov         eax,dword ptr [ebp+18h]
    0040EF0A   push        eax
    0040EF0B   mov         ecx,dword ptr [ebp-1Ch]
    0040EF0E   push        ecx
    0040EF0F   mov         edx,dword ptr [ebp+0Ch]
    0040EF12   push        edx
    0040EF13   mov         eax,dword ptr [ebp+8]
    0040EF16   push        eax
    0040EF17   call        __ArrayUnwind (004011a0)
    0040EF1C   ret
    0040EF1D   mov         ecx,dword ptr [ebp-10h]
    0040EF20   mov         dword ptr fs:[0],ecx
    0040EF27   pop         edi
    0040EF28   pop         esi
    0040EF29   pop         ebx
    0040EF2A   mov         esp,ebp
    0040EF2C   pop         ebp
    0040EF2D   ret         14h
  • 调用析构代理函数处的代码与析构1个对象的代码基本一致,只是push的标志使3而不再是1了。
  • 接下来到多个对象析构代理函数的内部去看看,这里调用了一个‘eh vector destructor iterator‘函数进行循环释放对象数组。
  • People::`vector deleting destructor‘:
               ; 函数入口代码, 与我们的分析无关, 忽略
    0040ED10   push        ebp
    0040ED11   mov         ebp,esp
    0040ED13   sub         esp,44h
    0040ED16   push        ebx
    0040ED17   push        esi
    0040ED18   push        edi
    0040ED19   push        ecx
    0040ED1A   lea         edi,[ebp-44h]
    0040ED1D   mov         ecx,11h
    0040ED22   mov         eax,0CCCCCCCCh
    0040ED27   rep stos    dword ptr [edi]
               ; 将对象首地址弹到ecx中
    0040ED29   pop         ecx
               ; 将首地址存放到[ebp - 4]中
    0040ED2A   mov         dword ptr [ebp-4],ecx
               ; 取得标志
    0040ED2D   mov         eax,dword ptr [ebp+8]
               ; 做按位与操作
    0040ED30   and         eax,2
               ; 如果为1, 就跳转到0x0040ED6F处只执行一次析构函数
    0040ED33   test        eax,eax
    0040ED35   je          People::`vector deleting destructor‘+5Fh (0040ed6f)
               ; 否则压入析构函数的地址
    0040ED37   push        offset @ILT+10(People::~People) (0040100f)
               ; 将对象首地址传入ecx中
    0040ED3C   mov         ecx,dword ptr [ebp-4]
               ; 将对象的个数存入edx中
    0040ED3F   mov         edx,dword ptr [ecx-4]
               ; 压入对象的个数
    0040ED42   push        edx
               ; 压入对象的大小
    0040ED43   push        1
               ; 将对象的首地址传入eax中
    0040ED45   mov         eax,dword ptr [ebp-4]
               ; 压入对象首地址
    0040ED48   push        eax
               ; 调用函数进行循环析构多个对象
    0040ED49   call        `eh vector destructor iterator‘ (0040edb0)
               ; 获取释放标志
    0040ED4E   mov         ecx,dword ptr [ebp+8]
               ; 检查是否释放堆空间
    0040ED51   and         ecx,1
    0040ED54   test        ecx,ecx
    0040ED56   je          People::`vector deleting destructor‘+57h (0040ed67)
               ; edx中保留了对象数组的首地址
    0040ED58   mov         edx,dword ptr [ebp-4]
               ; 修正为正确的包含对象个数的首地址, 因为即将调用delete函数, 而delete函数并没有记录对象的个数
    0040ED5B   sub         edx,4
    0040ED5E   push        edx
               ; 调用delete函数
    0040ED5F   call        operator delete (00401220)
               ; 堆栈平衡
    0040ED64   add         esp,4
               ; 修正为正确的包含对象个数的首地址, 并存入eax中
    0040ED67   mov         eax,dword ptr [ebp-4]
    0040ED6A   sub         eax,4
    0040ED6D   jmp         People::`vector deleting destructor‘+80h (0040ed90)
               ; 这里是只有一个对象的析构过程
               ; ecx中存放该对象的首地址
    0040ED6F   mov         ecx,dword ptr [ebp-4]
               ; 调用析构函数
    0040ED72   call        @ILT+10(People::~People) (0040100f)
               ; 获取释放标志
    0040ED77   mov         eax,dword ptr [ebp+8]
               ; 检查是否释放堆空间
    0040ED7A   and         eax,1
    0040ED7D   test        eax,eax
               ; 如果释放就跳转
    0040ED7F   je          People::`vector deleting destructor‘+7Dh (0040ed8d)
               ; 否则压入单个对象的首地址
    0040ED81   mov         ecx,dword ptr [ebp-4]
    0040ED84   push        ecx
               ; 调用delete函数
    0040ED85   call        operator delete (00401220)
               ; 堆栈平衡
    0040ED8A   add         esp,4
               ; eax中存放该对象的首地址
    0040ED8D   mov         eax,dword ptr [ebp-4]
               ; 函数收尾代码, 不做分析
    0040ED90   pop         edi
    0040ED91   pop         esi
    0040ED92   pop         ebx
    0040ED93   add         esp,44h
    0040ED96   cmp         ebp,esp
    0040ED98   call        __chkesp (004016b0)
    0040ED9D   mov         esp,ebp
    0040ED9F   pop         ebp
    0040EDA0   ret         4 
  • 跟进‘eh vector destructor iterator‘函数中。
  • ??[email protected]@[email protected]:
               ; 以下是函数入口的初始化和异常链的处理
    0040EDB0   push        ebp
    0040EDB1   mov         ebp,esp
    0040EDB3   push        0FFh
    0040EDB5   push        offset string "stream != NULL"+10h (00426048)
    0040EDBA   push        offset __except_handler3 (00407590)
    0040EDBF   mov         eax,fs:[00000000]
    0040EDC5   push        eax
    0040EDC6   mov         dword ptr fs:[0],esp
    0040EDCD   add         esp,0F4h
    0040EDD0   push        ebx
    0040EDD1   push        esi
    0040EDD2   push        edi
               ; 通过对下面代码的分析可以知道, [ebp - 1Ch]中用于存放循环的次数
    0040EDD3   mov         dword ptr [ebp-1Ch],0
               ; eax中存放每个对象的大小
    0040EDDA   mov         eax,dword ptr [ebp+0Ch]
               ; 乘上对象的个数, eax中存放总共的字节大小
    0040EDDD   imul        eax,dword ptr [ebp+10h]
               ; ecx中存放对象的首地址
    0040EDE1   mov         ecx,dword ptr [ebp+8]
               ; ecx中存放最后一个对象的下一个地址
    0040EDE4   add         ecx,eax
               ; 存放到[ebp + 8]中
    0040EDE6   mov         dword ptr [ebp+8],ecx
               ; 保存申请堆空间的次数
    0040EDE9   mov         dword ptr [ebp-4],0
               ; edx中存放对象的个数
    0040EDF0   mov         edx,dword ptr [ebp+10h]
               ; edx减1
    0040EDF3   sub         edx,1
               ; 重新放回[ebp + 10h]中
    0040EDF6   mov         dword ptr [ebp+10h],edx
               ; 检测是否小于0, 如果小于0, 就结束循环
    0040EDF9   cmp         dword ptr [ebp+10h],0
    0040EDFD   jl          `eh vector destructor iterator‘+60h (0040ee10)
               ; 以下两步操作将eax指向即将被析构的对象的首地址(从后往前进行析构)
    0040EDFF   mov         eax,dword ptr [ebp+8]
    0040EE02   sub         eax,dword ptr [ebp+0Ch]
               ; 以下两步操作将待析构的对象首地址存入ecx中, 并且更新[ebp + 8]中的地址, 用于析构下一个对象
    0040EE05   mov         dword ptr [ebp+8],eax
    0040EE08   mov         ecx,dword ptr [ebp+8]
               ; 调用析构函数
    0040EE0B   call        dword ptr [ebp+14h]
               ; 重新跳回析构循环中
    0040EE0E   jmp         `eh vector destructor iterator‘+40h (0040edf0)
               ; 剩余代码与我们的分析无关, 这里不考究
    0040EE10   mov         dword ptr [ebp-1Ch],1
    0040EE17   mov         dword ptr [ebp-4],0FFFFFFFFh
    0040EE1E   call        `eh vector destructor iterator‘+75h (0040ee25)
    0040EE23   jmp         `eh vector destructor iterator‘+91h (0040ee41)
    0040EE25   cmp         dword ptr [ebp-1Ch],0
    0040EE29   jne         `eh vector destructor iterator‘+90h (0040ee40)
    0040EE2B   mov         ecx,dword ptr [ebp+14h]
    0040EE2E   push        ecx
    0040EE2F   mov         edx,dword ptr [ebp+10h]
    0040EE32   push        edx
    0040EE33   mov         eax,dword ptr [ebp+0Ch]
    0040EE36   push        eax
    0040EE37   mov         ecx,dword ptr [ebp+8]
    0040EE3A   push        ecx
    0040EE3B   call        __ArrayUnwind (004011a0)
    0040EE40   ret
    0040EE41   mov         ecx,dword ptr [ebp-10h]
    0040EE44   mov         dword ptr fs:[0],ecx
    0040EE4B   pop         edi
    0040EE4C   pop         esi
    0040EE4D   pop         ebx
    0040EE4E   mov         esp,ebp
    0040EE50   pop         ebp
    0040EE51   ret         10h 
  • 资料引用:
    • 钱林松 赵海旭. 《C++反汇编与逆向技术揭秘》. 机械工业出版社. 2012
  • 如有错误,欢迎指正,谢谢。
时间: 2024-10-12 14:15:37

C++中堆对象的构造函数和析构函数逆向分析的相关文章

c++学习笔记5,多重继承中派生类的构造函数与析构函数的调用顺序(二)

现在来测试一下在多重继承,虚继承,MI继承中虚继承中构造函数的调用情况. 先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace std; class base { public: base() { cout<<"base created!"<<endl; }

对C++中派生类的构造函数和析构函数的认识

一:构造函数 形式:派生类名::派生类名:基类名1(参数1),基类名2(参数2),--基类名n(参数n),数据成员1(参数1),数据成员2(参数2),--数据成员n(参数n){ 各种操作的说明 } 执行过程:先执行基类的构造函数,再进行数据成员的赋值,最后执行函数体. 其中基类名和数据成员的顺序是由在派生类的定义中声明的顺序决定执行的顺序的,因此它们的顺序是任意的,但为了可读性,还是最好按顺序写. 如果基类只有默认构造函数,则基类名和参数表可以不用写出来. 二:复制构造函数 派生类的构造函数的形

C#中Monitor对象与Lock关键字的区别分析

这篇文章主要介绍了C#中Monitor对象与Lock关键字的区别,需要的朋友可以参考下 Monitor对象 1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit()).2.Monitor的常用属性和方法: Enter(Object)

PHP类和对象之构造函数和析构函数

PHP5可以在类中使用__construct()定义一个构造函数,具有构造函数的类,会在每次对象创建的时候调用该函数,因此常用来在对象创建的时候进行一些初始化工作. class Car { function __construct() { print "构造函数被调用\n"; } } $car = new Car(); //实例化的时候 会自动调用构造函数__construct,这里会输出一个字符串 在子类中如果定义了__construct则不会调用父类的__construct,如果需

Js中的对象、构造函数、原型、原型链及继承

1.对象 在传统的面向过程的程序设计中,会造成函数或变量的冗余.而JS中对象的目的是将所有的具有相同属性或行为的代码整合到一起,形成一个集合,这样就会方便我们管理,例如: var person1={    name:"tan",    age:26,    showMessage:function(){        alert("name:"+this.name);    }};person.showMessage();//输出name:tan 以上的例子将nam

PHP对象2: 构造函数与析构函数

当一个对象的所有引用都没有时, 一个对象才消失, 这时才执行析构函数 <?php class firecat{ public $name; function say(){ echo 'I love Perl6!<br/>'; } #构造函数 function __construct(){ echo '一个对象创造了.<br />'; } #析构函数 #没有参数也没返回值 function __destruct(){ echo '一个对象消失了.<br />'; }

转:C#中Monitor对象与Lock关键字的区别分析

Monitor对象1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit()).2.Monitor的常用属性和方法: Enter(Object) 在指定对象上获取排他锁. Exit(Object) 释放指定对象上的排他锁. IsEnte

php面向对象构造函数,析构函数

在php面向对象中有构造函数和析构函数 构造函数使用__construct()书写,它允许开发者在一个类中定义一个方法作为构造函数,具有构造函数的类会在每次创建新对象时先调用此方法,所以适合在使用对象之前做一些初始化工作.构造函数被创建时会自动调用,可以传递参数和设任参数默认值,其也可以调用属性和方法,和被其它方法显示调用.注:一个类中只能有一个构造函数. 析构函数使用__destruct()书写,是在垃圾对象被回收时执行,在也就是在对象销毁时自动调用,没有参数值和返回值,析构函数由系统自动调用

构造函数和析构函数能不能被继承

1.构造函数和析构函数不能被继承.构造函数和析构函数是用来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么.所以,在整个层次中的所有的构造函数和析构函数都必须被调用而不能被继承.2.子类的构造函数会显示的调用父类的构造函数或隐式的调用父类的默认的构造函数进行父类部分的初始化.3.析构函数也一样.它们都是每个类都有的东西,如果能被继承,那就没有办法初始化了. 不能重载析构函数,只能声明为虚函数,为了多态发生时能够完全析构只有构造函数才能重载,用于多种方式构造对象 如果通过基类指针动