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

我的引擎里面的一段代码!基本能阐述清楚原理了。睡觉咯~~~

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值