利用C++的operator new實現同一對象多次調用構造函數

struct STest

{

    STest( void )

    {

        ++iCount;

    }

 

    int iCount;

}

 

int main( void )

{

    Stest obj;

    obj.iCount = 0;

    new( static_cast< void* >( &obj ) ) Stest();

 

    return 0;

}

 

隨便寫了一個例子,能說明問題就行。

上面的紅色代碼調用了構造函數,由於構造函數中爲了計數,因此在再次調用構造函數之前先收工初始化成0.藍色那段代碼就是主題了。首先這裏會調用operator new( size_t, void* ) thow()。這個函數的原型是:

inline void *__cdecl operator new(size_t, void *_Where) _THROW0()
 { // construct array with placement at _Where
     return (_Where);
 }

這裏並沒有開闢新的空間。直接就返回了!那爲什麼後面還跟了個Stest()呢?而且語法也沒有錯誤。在new操作符執行完後。返回的就是我們傳進去的obj對象的地址。既然寫了Stest()那肯定就是要調用的。

但是這裏是創建新的對象呢?還是本來就是原來的對象呢?這裏只能反彙編裏面分析了。

首先看main函數:

00417A60  push        ebp 
00417A61  mov         ebp,esp
00417A63  push        0FFFFFFFFh
00417A65  push        offset __ehhandler$_main (425954h)
00417A6A  mov         eax,dword ptr fs:[00000000h]
00417A70  push        eax 
00417A71  mov         dword ptr fs:[0],esp
00417A78  sub         esp,0E8h
00417A7E  push        ebx 
00417A7F  push        esi 
00417A80  push        edi 
00417A81  lea          edi,[ebp-0F4h]
00417A87  mov         ecx,3Ah
00417A8C  mov         eax,0CCCCCCCCh
00417A91  rep stos    dword ptr [edi]
00417A93  lea          ecx,[obj]
00417A96  call         STest::STest (4115DCh)
00417A9B  mov         dword ptr [obj],0
00417AA2  lea         eax,[obj]
00417AA5  push        eax 
00417AA6  push        4   
00417AA8  call        operator new (411096h)

00417AAD  add         esp,8
00417AB0  mov         dword ptr [ebp-0E0h],eax   // new 操作符的返回值
00417AB6  mov         dword ptr [ebp-4],0
00417ABD  cmp         dword ptr [ebp-0E0h],0
00417AC4  je          main+79h (417AD9h)
00417AC6  mov         ecx,dword ptr [ebp-0E0h]   // 將返回值給ECX
00417ACC  call        STest::STest (4115DCh)      // 調用構造函數
00417AD1  mov         dword ptr [ebp-0F4h],eax
00417AD7  jmp         main+83h (417AE3h)
00417AD9  mov         dword ptr [ebp-0F4h],0
00417AE3  mov         ecx,dword ptr [ebp-0F4h]
00417AE9  mov         dword ptr [ebp-0ECh],ecx
00417AEF  mov         dword ptr [ebp-4],0FFFFFFFFh
00417AF6  xor         eax,eax
00417AF8  push        edx 
00417AF9  mov         ecx,ebp
00417AFB  push        eax 
00417AFC  lea         edx,ds:[417B27h]
00417B02  call        @ILT+480(@_RTC_CheckStackVars@8) (4111E5h)
00417B07  pop         eax 
00417B08  pop         edx 
00417B09  mov         ecx,dword ptr [ebp-0Ch]
00417B0C  mov         dword ptr fs:[0],ecx
00417B13  pop         edi 
00417B14  pop         esi 
00417B15  pop         ebx 
00417B16  add         esp,0F4h
00417B1C  cmp         ebp,esp
00417B1E  call        @ILT+1095(__RTC_CheckEsp) (41144Ch)
00417B23  mov         esp,ebp
00417B25  pop         ebp 
00417B26  ret  

 

首先紅色的指令是調用new操作符。完成之後將返回值eax放到ebp-0E0h中,第二條藍色的指令又把裏面的值給了ECX。這裏的目的就是爲了在構造函數中pop ecx。 這裏就關係到類對象調用成員函數的反彙編層面調用步驟。首先會將對象的地址給ECX。一個成員函數內部會比普通的函數多兩條指令。就是push ecx和pop ecx。先看看STest構造函數的反彙編代碼:

00411D90  push        ebp 
00411D91  mov         ebp,esp
00411D93  sub         esp,0CCh
00411D99  push        ebx 
00411D9A  push        esi 
00411D9B  push        edi 
00411D9C  push        ecx 
00411D9D  lea         edi,[ebp-0CCh]
00411DA3  mov         ecx,33h
00411DA8  mov         eax,0CCCCCCCCh
00411DAD  rep stos    dword ptr [edi]
00411DAF  pop         ecx 
00411DB0  mov         dword ptr [ebp-8],ecx 
00411DB3  mov         eax,dword ptr [this]
00411DB6  mov         ecx,dword ptr [eax]
00411DB8  add         ecx,1
00411DBB  mov         edx,dword ptr [this]
00411DBE  mov         dword ptr [edx],ecx
00411DC0  mov         eax,dword ptr [this]
00411DC3  pop         edi 
00411DC4  pop         esi 
00411DC5  pop         ebx 
00411DC6  mov         esp,ebp
00411DC8  pop         ebp 
00411DC9  ret

紅色的push是爲了先把ecx讓出來執行藍色的mov ecx, 33h。沒有辦法,別人要用肯定先把自己的值給壓棧保存。在別人用完了後,會執行紅色的pop ecx。將剛纔壓入的ecx的值重新彈出到ecx中!再看下面綠色的兩條指令,將ecx給了[ebp-8]這裏剛好就是結構體對象的第一個字節的地址。原理就不多說了!這下this指針就是指向的剛開始傳進來的obj對象的地址了。之後就是加1.當然還可以其他操作。呵呵。便實現了構造函數多次調用!

這種一般用在申請已有空間等情況下:

template< class Ty >
    class ALGA_API CAlgaAllocator
    {
    public:
        CAlgaAllocator( void ){}
        virtual ~CAlgaAllocator( void ){}
   
    public:
        Ty* allocate( size_t size )
        {
            return ( Ty* )internal_new( size * sizeof( Ty ) );
        }

        void deallocate( Ty* ptr )
        {
            internal_delete( ptr );
        }

        void construct( Ty* ptr, const Ty& elem )
        {
            new ( ( void* )ptr ) Ty( elem );
        }

        void destruct( Ty* ptr )
        {
            ptr->~Ty();
        }

    protected:
        virtual void* internal_new( size_t size )
        {
            return operator  new( size );
        }

        virtual void internal_delete( void* ptr )
        {
            operator  delete( ptr );
        }
    };

我的引擎裏面的一段代碼!基本能闡述清楚原理了。睡覺咯~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章