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

來源:互聯網
上載者:User

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 );
        }
    };

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.