| 
UID2精华积分7982威望 点宅币 个贡献 次宅之契约 份最后登录1970-1-1在线时间 小时 
 | 
 
| 下面仅以VS调试子功能——查看汇编代码进行分析,以VS2012源码为例: 
 复制代码
int main( ) 
{
00E282E0  push        ebp  
00E282E1  mov         ebp,esp  
00E282E3  push        0FFFFFFFFh  
00E282E5  push        0E29400h  
00E282EA  mov         eax,dword ptr fs:[00000000h]  
00E282F0  push        eax  
00E282F1  sub         esp,220h  
00E282F7  push        ebx  
00E282F8  push        esi  
00E282F9  push        edi  
00E282FA  lea         edi,[ebp-22Ch]  
00E28300  mov         ecx,88h  
00E28305  mov         eax,0CCCCCCCCh  
00E2830A  rep stos    dword ptr es:[edi]  
00E2830C  mov         eax,dword ptr ds:[00E300E0h]  
00E28311  xor         eax,ebp  
00E28313  mov         dword ptr [ebp-10h],eax  
00E28316  push        eax  
00E28317  lea         eax,[ebp-0Ch]  
00E2831A  mov         dword ptr fs:[00000000h],eax  
        MyClass* fPtr1 = new MyClass;
00E28320  push        4  
        MyClass* fPtr1 = new MyClass;
00E28322  call        operator new (0E21410h)  
00E28327  add         esp,4  
00E2832A  mov         dword ptr [ebp-218h],eax  
00E28330  mov         dword ptr [ebp-4],0  
00E28337  cmp         dword ptr [ebp-218h],0  
00E2833E  je          main+73h (0E28353h)  
00E28340  mov         ecx,dword ptr [ebp-218h]  
00E28346  call        MyClass::MyClass (0E210BEh)  
00E2834B  mov         dword ptr [ebp-22Ch],eax  
00E28351  jmp         main+7Dh (0E2835Dh)  
00E28353  mov         dword ptr [ebp-22Ch],0  
00E2835D  mov         eax,dword ptr [ebp-22Ch]  
00E28363  mov         dword ptr [ebp-224h],eax  
00E28369  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E28370  mov         ecx,dword ptr [ebp-224h]  
00E28376  mov         dword ptr [fPtr1],ecx  
        delete fPtr1;
00E28379  mov         eax,dword ptr [fPtr1]  
00E2837C  mov         dword ptr [ebp-200h],eax  
00E28382  mov         ecx,dword ptr [ebp-200h]  
00E28388  mov         dword ptr [ebp-20Ch],ecx  
00E2838E  cmp         dword ptr [ebp-20Ch],0  
00E28395  je          main+0CCh (0E283ACh)  
00E28397  push        1  
00E28399  mov         ecx,dword ptr [ebp-20Ch]  
00E2839F  call        MyClass::`scalar deleting destructor' (0E2101Eh)  
00E283A4  mov         dword ptr [ebp-22Ch],eax  
00E283AA  jmp         main+0D6h (0E283B6h)  
00E283AC  mov         dword ptr [ebp-22Ch],0  
        MyClass* fPtr2 = new( nothrow ) MyClass;
00E283B6  push        0E30228h  
00E283BB  push        4  
00E283BD  call        operator new (0E210E1h)  
00E283C2  add         esp,8  
00E283C5  mov         dword ptr [ebp-1E8h],eax  
00E283CB  mov         dword ptr [ebp-4],1  
00E283D2  cmp         dword ptr [ebp-1E8h],0  
00E283D9  je          main+10Eh (0E283EEh)  
00E283DB  mov         ecx,dword ptr [ebp-1E8h]  
00E283E1  call        MyClass::MyClass (0E210BEh)  
00E283E6  mov         dword ptr [ebp-22Ch],eax  
00E283EC  jmp         main+118h (0E283F8h)  
00E283EE  mov         dword ptr [ebp-22Ch],0  
00E283F8  mov         eax,dword ptr [ebp-22Ch]  
00E283FE  mov         dword ptr [ebp-1F4h],eax  
00E28404  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E2840B  mov         ecx,dword ptr [ebp-1F4h]  
00E28411  mov         dword ptr [fPtr2],ecx  
        delete fPtr2;
00E28414  mov         eax,dword ptr [fPtr2]  
00E28417  mov         dword ptr [ebp-1D0h],eax  
00E2841D  mov         ecx,dword ptr [ebp-1D0h]  
00E28423  mov         dword ptr [ebp-1DCh],ecx  
00E28429  cmp         dword ptr [ebp-1DCh],0  
00E28430  je          main+167h (0E28447h)  
00E28432  push        1  
00E28434  mov         ecx,dword ptr [ebp-1DCh]  
00E2843A  call        MyClass::`scalar deleting destructor' (0E2101Eh)  
00E2843F  mov         dword ptr [ebp-22Ch],eax  
00E28445  jmp         main+171h (0E28451h)  
00E28447  mov         dword ptr [ebp-22Ch],0  
        char x1[sizeof( MyClass )];
        MyClass* fPtr3 = new( &x1[0] ) MyClass;
00E28451  mov         eax,1  
00E28456  imul        eax,eax,0  
00E28459  lea         ecx,x1[eax]  
00E2845D  push        ecx  
00E2845E  push        4  
00E28460  call        operator new (0E2105Ah)  
00E28465  add         esp,8  
00E28468  mov         dword ptr [ebp-1B8h],eax  
00E2846E  mov         dword ptr [ebp-4],2  
00E28475  cmp         dword ptr [ebp-1B8h],0  
00E2847C  je          main+1B1h (0E28491h)  
00E2847E  mov         ecx,dword ptr [ebp-1B8h]  
00E28484  call        MyClass::MyClass (0E210BEh)  
00E28489  mov         dword ptr [ebp-22Ch],eax  
00E2848F  jmp         main+1BBh (0E2849Bh)  
00E28491  mov         dword ptr [ebp-22Ch],0  
00E2849B  mov         edx,dword ptr [ebp-22Ch]  
00E284A1  mov         dword ptr [ebp-1C4h],edx  
00E284A7  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E284AE  mov         eax,dword ptr [ebp-1C4h]  
00E284B4  mov         dword ptr [fPtr3],eax  
        fPtr3 -> ~MyClass();
00E284B7  push        0  
00E284B9  mov         ecx,dword ptr [fPtr3]  
00E284BC  call        MyClass::`scalar deleting destructor' (0E2101Eh)  
        
        MyClass* fPtr4 = new MyClass[2];
00E284C1  push        0Ch  
00E284C3  call        operator new[] (0E2119Ah)  
00E284C8  add         esp,4  
00E284CB  mov         dword ptr [ebp-1A0h],eax  
00E284D1  mov         dword ptr [ebp-4],3  
00E284D8  cmp         dword ptr [ebp-1A0h],0  
00E284DF  je          main+23Bh (0E2851Bh)  
00E284E1  mov         eax,dword ptr [ebp-1A0h]  
        
        MyClass* fPtr4 = new MyClass[2];
00E284E7  mov         dword ptr [eax],2  
00E284ED  push        0E21023h  
00E284F2  push        0E210BEh  
00E284F7  push        2  
00E284F9  push        4  
00E284FB  mov         ecx,dword ptr [ebp-1A0h]  
00E28501  add         ecx,4  
00E28504  push        ecx  
00E28505  call        `eh vector constructor iterator' (0E21316h)  
00E2850A  mov         edx,dword ptr [ebp-1A0h]  
00E28510  add         edx,4  
00E28513  mov         dword ptr [ebp-22Ch],edx  
00E28519  jmp         main+245h (0E28525h)  
00E2851B  mov         dword ptr [ebp-22Ch],0  
00E28525  mov         eax,dword ptr [ebp-22Ch]  
00E2852B  mov         dword ptr [ebp-1ACh],eax  
00E28531  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E28538  mov         ecx,dword ptr [ebp-1ACh]  
00E2853E  mov         dword ptr [fPtr4],ecx  
        delete []fPtr4;
00E28541  mov         eax,dword ptr [fPtr4]  
00E28544  mov         dword ptr [ebp-188h],eax  
00E2854A  mov         ecx,dword ptr [ebp-188h]  
00E28550  mov         dword ptr [ebp-194h],ecx  
00E28556  cmp         dword ptr [ebp-194h],0  
00E2855D  je          main+294h (0E28574h)  
00E2855F  push        3  
00E28561  mov         ecx,dword ptr [ebp-194h]  
00E28567  call        MyClass::`vector deleting destructor' (0E2128Ah)  
00E2856C  mov         dword ptr [ebp-22Ch],eax  
00E28572  jmp         main+29Eh (0E2857Eh)  
00E28574  mov         dword ptr [ebp-22Ch],0  
        MyClass* fPtr5 = new( nothrow ) MyClass[2];
00E2857E  push        0E30228h  
00E28583  push        0Ch  
00E28585  call        operator new[] (0E21311h)  
00E2858A  add         esp,8  
00E2858D  mov         dword ptr [ebp-170h],eax  
00E28593  mov         dword ptr [ebp-4],4  
00E2859A  cmp         dword ptr [ebp-170h],0  
00E285A1  je          main+2FDh (0E285DDh)  
00E285A3  mov         eax,dword ptr [ebp-170h]  
00E285A9  mov         dword ptr [eax],2  
00E285AF  push        0E21023h  
00E285B4  push        0E210BEh  
00E285B9  push        2  
00E285BB  push        4  
00E285BD  mov         ecx,dword ptr [ebp-170h]  
00E285C3  add         ecx,4  
00E285C6  push        ecx  
00E285C7  call        `eh vector constructor iterator' (0E21316h)  
00E285CC  mov         edx,dword ptr [ebp-170h]  
00E285D2  add         edx,4  
00E285D5  mov         dword ptr [ebp-22Ch],edx  
00E285DB  jmp         main+307h (0E285E7h)  
00E285DD  mov         dword ptr [ebp-22Ch],0  
00E285E7  mov         eax,dword ptr [ebp-22Ch]  
00E285ED  mov         dword ptr [ebp-17Ch],eax  
00E285F3  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E285FA  mov         ecx,dword ptr [ebp-17Ch]  
00E28600  mov         dword ptr [fPtr5],ecx  
        delete []fPtr5;
00E28603  mov         eax,dword ptr [fPtr5]  
00E28606  mov         dword ptr [ebp-158h],eax  
00E2860C  mov         ecx,dword ptr [ebp-158h]  
00E28612  mov         dword ptr [ebp-164h],ecx  
00E28618  cmp         dword ptr [ebp-164h],0  
00E2861F  je          main+356h (0E28636h)  
00E28621  push        3  
00E28623  mov         ecx,dword ptr [ebp-164h]  
00E28629  call        MyClass::`vector deleting destructor' (0E2128Ah)  
00E2862E  mov         dword ptr [ebp-22Ch],eax  
00E28634  jmp         main+360h (0E28640h)  
00E28636  mov         dword ptr [ebp-22Ch],0  
        char x2[2*sizeof( MyClass ) + sizeof(int)];
        MyClass* fPtr6 = new ( &x2[0] ) MyClass[2];
00E28640  mov         eax,1  
00E28645  imul        eax,eax,0  
00E28648  lea         ecx,x2[eax]  
00E2864C  push        ecx  
00E2864D  push        0Ch  
00E2864F  call        operator new[] (0E21389h)  
00E28654  add         esp,8  
00E28657  mov         dword ptr [ebp-140h],eax  
00E2865D  mov         dword ptr [ebp-4],5  
00E28664  cmp         dword ptr [ebp-140h],0  
00E2866B  je          main+3C7h (0E286A7h)  
00E2866D  mov         edx,dword ptr [ebp-140h]  
00E28673  mov         dword ptr [edx],2  
00E28679  push        0E21023h  
00E2867E  push        0E210BEh  
00E28683  push        2  
00E28685  push        4  
00E28687  mov         eax,dword ptr [ebp-140h]  
00E2868D  add         eax,4  
00E28690  push        eax  
00E28691  call        `eh vector constructor iterator' (0E21316h)  
00E28696  mov         ecx,dword ptr [ebp-140h]  
00E2869C  add         ecx,4  
00E2869F  mov         dword ptr [ebp-22Ch],ecx  
00E286A5  jmp         main+3D1h (0E286B1h)  
00E286A7  mov         dword ptr [ebp-22Ch],0  
00E286B1  mov         edx,dword ptr [ebp-22Ch]  
00E286B7  mov         dword ptr [ebp-14Ch],edx  
00E286BD  mov         dword ptr [ebp-4],0FFFFFFFFh  
00E286C4  mov         eax,dword ptr [ebp-14Ch]  
00E286CA  mov         dword ptr [fPtr6],eax  
        fPtr6[1].~MyClass();
00E286CD  push        0  
00E286CF  mov         ecx,4  
00E286D4  shl         ecx,0  
00E286D7  add         ecx,dword ptr [fPtr6]  
00E286DA  call        MyClass::`scalar deleting destructor' (0E2101Eh)  
        fPtr6[0].~MyClass();//按照和构造顺序相反顺序析构
00E286DF  push        0  
00E286E1  mov         ecx,4  
00E286E6  imul        ecx,ecx,0  
00E286E9  add         ecx,dword ptr [fPtr6]  
00E286EC  call        MyClass::`scalar deleting destructor' (0E2101Eh)  
}
00E286F1  xor         eax,eax  
}
00E286F3  push        edx  
00E286F4  mov         ecx,ebp  
00E286F6  push        eax  
00E286F7  lea         edx,ds:[0E28730h]  
00E286FD  call        @_RTC_CheckStackVars@8 (0E21145h)  
00E28702  pop         eax  
00E28703  pop         edx  
00E28704  mov         ecx,dword ptr [ebp-0Ch]  
00E28707  mov         dword ptr fs:[0],ecx  
00E2870E  pop         ecx  
00E2870F  pop         edi  
00E28710  pop         esi  
00E28711  pop         ebx  
00E28712  mov         ecx,dword ptr [ebp-10h]  
00E28715  xor         ecx,ebp  
00E28717  call        @__security_check_cookie@4 (0E21050h)  
00E2871C  add         esp,22Ch  
00E28722  cmp         ebp,esp  
00E28724  call        __RTC_CheckEsp (0E2135Ch)  
00E28729  mov         esp,ebp  
00E2872B  pop         ebp  
00E2872C  ret 
 
 
 第一种形式为常规new,MyClass* fPtr1 = new MyClass;实现方式:
 单步步入后IDE定位到new.cpp的
 
 复制代码
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{       // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
    if (_callnewh(size) == 0)
    {       // report no memory
        _THROW_NCEE(_XSTD bad_alloc, );
    }
    return (p);
}
而步过之后直接跳转到构造函数中。
 从上面new函数实现可以看到是使用malloc函数进行分配,如果失败则调用_callnewh调用注册过的“new操作符失败回调”函数(注册用_set_new_handler),如果原先没注册new失败回调,则抛出bad_alloc异常,可见在默认情况下,该while只会执行1次,仅当自定义new失败回调函数返回true,才可能多次尝试分配。
 该种形式delete实现方式,单步以后vs不能定位到源码,不过我们可以换一种思路,既然知道一定执行析构函数,那么就在析构中下断点,断下后查看反汇编,并执行到上一级调用即可找到delete实现方法,因此看汇编实现,发现是一个名为“scalar deleting destructor”的内部函数:
 
 复制代码
00EB3470  push        ebp  
00EB3471  mov         ebp,esp  
00EB3473  sub         esp,0CCh  
00EB3479  push        ebx  
00EB347A  push        esi  
00EB347B  push        edi  
00EB347C  push        ecx  
00EB347D  lea         edi,[ebp-0CCh]  
00EB3483  mov         ecx,33h  
00EB3488  mov         eax,0CCCCCCCCh  
00EB348D  rep stos    dword ptr es:[edi]  
00EB348F  pop         ecx  //以上部分为debug版API常见头,无需理会
00EB3490  mov         dword ptr [this],ecx  
00EB3493  mov         ecx,dword ptr [this]  
00EB3496  call        MyClass::~MyClass (0EB1023h)  //执行析构
00EB349B  mov         eax,dword ptr [ebp+8]  
00EB349E  and         eax,1  
00EB34A1  je          MyClass::`scalar deleting destructor'+3Fh (0EB34AFh)  //如果传入参数允许释放则进行调用对应delete函数(对于定位new对应的delete该参数是设置为不允许的)
00EB34A3  mov         eax,dword ptr [this]  
00EB34A6  push        eax  
00EB34A7  call        operator delete (0EB1154h)  
00EB34AC  add         esp,4  
00EB34AF  mov         eax,dword ptr [this]  //以下是无关的收尾工作
00EB34B2  pop         edi  
00EB34B3  pop         esi  
00EB34B4  pop         ebx  
00EB34B5  add         esp,0CCh  
00EB34BB  cmp         ebp,esp  
00EB34BD  call        __RTC_CheckEsp (0EB1352h)  
00EB34C2  mov         esp,ebp  
00EB34C4  pop         ebp  
00EB34C5  ret         4 
 
 执行到call operator delete这行,步入之后转到源码,可以看到使用的是dbgdel.cpp的delete函数。实现如下:
 
 复制代码
void operator delete( void *pUserData )
{
        _CrtMemBlockHeader * pHead;
        RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
        if (pUserData == NULL)
            return;
        _mlock(_HEAP_LOCK);  /* 阻塞其他线程*/
        __TRY
            /* 得到用于内存块信息头指针*/
            pHead = pHdr(pUserData);
             /* 检查区块类型 */
            _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
            _free_dbg( pUserData, pHead->nBlockUse );//调用free函数释放内存
        __FINALLY
            _munlock(_HEAP_LOCK);  /* 解锁其他线程*/
        __END_TRY_FINALLY
        return;
}
 第二种方式为不抛出异常的new,MyClass* fPtr2 = new( nothrow ) MyClass;实现方式:
 单步到newopnt.cpp中,
 
 复制代码
void * __CRTDECL operator new(size_t count, const std::nothrow_t&) _THROW0()
{        // try to allocate count bytes
        void *p;
        _TRY_BEGIN
        p = operator new(count);
        _CATCH_ALL
        p = 0;
        _CATCH_END
        return (p);
}
 #define _TRY_BEGIN        try {
 #define _CATCH(x)        } catch (x) {
 #define _CATCH_ALL        } catch (...) {
 #define _CATCH_END        }
可见,这里用try块捕获了异常,因此不再抛出异常,余下的就是调用常规new函数而已。
 
 第三种方式为定位new,
 char x1[sizeof( MyClass )];
 MyClass* fPtr3 = new( &x1[0] ) MyClass;
 这里所谓的定位就是说告诉new我们已经有一个内存位置了:
 单步后发现new位于new文件:
 
 复制代码
inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0()
{        // construct array with placement at _Where
        return (_Where);
}
可以发现该new什么都没做,那么为什么还要new呢?仔细想想可以知道,编译器对new的处理是调用new函数后,之后将该地址作为this指针进行初始化操作(比如设置虚表),再调用构造函数,而构造函数这玩意不能直接调用,不像析构函数那样,因为构造之前还没有对象和指针呢,对象和指针是构造以后才有的,而调用析构函数的时候,是已经有对象或指针的。所以这种定位new在我理解,就是可以相当于可以直接构造了。
 从上面可以看到new操作符先调用合适的new函数分配空间,之后调用构造函数构造,而delete函数刚好相对,先进行析构之后调用析构函数析构;同时可以看到布局new操作符的好处是可以手动指定构造和析构的时间,对于new无论哪种形式,在调用new函数分配好内存后都会调用构造函数进行构造,而定位new函数实则是直接返回,这就导致直接使用当前地址进行构造,相当于显示调用构造函数,而析构时由于没有实际分配空间,因此不能用delete,而是显示调用析构函数进行析构。
 上面都是对于有构造函数和析构函数对象的情况,用delete时,编译器会为该类专门生成一个scalar deleting destructor函数,该函数中先进行析构,之后调用operator delete函数。当然,如果没有析构函数,那么就不会有scalar deleting destructor函数了,此时单步是可以看到delete源码的,即dbgdel.cpp中的void operator delete(void *pUserData)函数。这一点在delete用于基本类型时显而易见。
 
 下面再来看数组的情况
 第一种类型new,MyClass* fPtr4 = new MyClass[2],单步以后定位到newaop.cpp中:
 
 复制代码
void *__CRTDECL operator new[](size_t count) _THROW1(std::bad_alloc)
{        // try to allocate count bytes for an array
        return (operator new(count));
}
而编译器传给该new[]函数的参数count是sizeof(MyClass[2])+sizeof(int)    该sizeof(int)用于内存管理
 new[]()仍然调用了new();没有本质区别,即这么多对象占用的内存是当作整体分配的。再分配好之后,就需要对每个对象this指针处进行初始化和构造了。
 
 这些都是边研究边写的,剩下2种情况下次讲述
 | 
 |