C++ 内存分配(new,operator new)面面观 (四)

2014-11-23 23:33:58 · 作者: · 浏览: 27
}
#endif
#endif


那么它究竟有什么用呢?事实上,它可以实现在ptr所指地址上构建一个对象(通过调用其构造函数),这在内存池技术上有广泛应用。
它的调用形式为:
new(p) A(); //也可用A(5)等有参构造函数。
前面说到,new运算符都会调用operator new,而这里的operator new(size_t, void*)并没有什么作用,真正起作用的是new运算符的第二个步骤:在p处调用A构造函数。这里的p可以是动态分配的内存,也可以是栈中缓冲,如char buf[100]; new(buf) A();


我们仍然可以通过一个例子来验证:
[cpp]
#include
class A
{
public:
A()
{
std::cout<<"call A constructor"< }

~A()
{
std::cout<<"call A destructor"< }
};
int _tmain(int argc, _TCHAR* argv[])
{

A* p = (A*)::operator new(sizeof(A)); //分配

new(p) A(); //构造

p->~A(); //析构

::operator delete(p); //释放

system("pause");
return 0;
}

#include
class A
{
public:
A()
{
std::cout<<"call A constructor"< }

~A()
{
std::cout<<"call A destructor"< }
};
int _tmain(int argc, _TCHAR* argv[])
{

A* p = (A*)::operator new(sizeof(A)); //分配

new(p) A(); //构造

p->~A(); //析构

::operator delete(p); //释放

system("pause");
return 0;
}

上面的代码将对象的分配,构造,析构和释放分离开来,这也是new和delete运算符两句就能完成的操作。
先直接运行可以看到程序输出:


再分别注释掉new(a) A();和a->~A();两句,可以看到对应的构造和析构函数将不会被调用。
然后查看反汇编:
平台: Visual Studio 2008 Debug版
[plain]
A* a = (A*)::operator new(sizeof(A)); //分配
00F9151D push 1
00F9151F call operator new (0F91208h) ;调用::operator new(size_t size)也就是throwing(1)版本
00F91524 add esp,4
00F91527 mov dword ptr [ebp-14h],eax ;返回地址放入[ebp-14h] 即为p

new(a) A(); //构造
00F9152A mov eax,dword ptr [ebp-14h]
00F9152D push eax
00F9152E push 1 ;压入p
00F91530 call operator new (0F91280h);调用operator new(size_t, void* p)即placement(3)版本 只是简单返回p
00F91535 add esp,8
00F91538 mov dword ptr [ebp-0E0h],eax ;将p放入[ebp-0E0h]
00F9153E mov dword ptr [ebp-4],0
00F91545 cmp dword ptr [ebp-0E0h],0 ;判断p是否为空
00F9154C je wmain+81h (0F91561h) ;如果为空 跳过构造函数
00F9154E mov ecx,dword ptr [ebp-0E0h] ;取出p到ecx
00F91554 call A::A (0F91285h) ;调用构造函数 根据_thiscall调用约定 this指针通过ecx寄存器传递
00F91559 mov dword ptr [ebp-0F4h],eax ;将返回值(this指针)放入[ebp-0F4h]中
00F9155F jmp wmain+8Bh (0F9156Bh) ;跳过下一句
00F91561 mov dword ptr [ebp-0F4h],0 ;将[ebp-0F4h]置空 当前面判断p为空时执行此语句
00F9156B mov ecx,dword ptr [ebp-0F4h] ;[ebp-0F4h]为最终构造完成后的this指针(或者为空) 放入ecx
00F91571 mov dword ptr [ebp-0ECh],ecx ;又将this放入[ebp-0ECh] 这些都是调试所用
00F91577 mov dword ptr [ebp-4],0FFFFFFFFh

a->~A(); //析构
00F9157E push 0
00F91580 mov ecx,dword ptr [ebp-14h] ;从[ebp-14h]中取出p
00F91583 call A::`scalar deleting destructor' (0F91041h) ;调用析构函数(跟踪进去比较复杂 如果在Release下,构造析构函数都是直接展开的)

::operator delete(a); //释放
00F91588 mov eax,dword ptr [ebp-14h] ;将p放入eax
00F9158B push eax ;压入p
00F9158C call operator delete (0F910B9h);调用operator delete(void* )
00F91591 add esp,4

A* a = (A*)::operator new(sizeof(A)); //分配
00F9151D push 1
00F9151F call operator new (0F91208h) ;调用::operator new(size_t size)也就是throwing(1)版本
00F91524 add esp,4
00F91527 mov dword ptr [ebp-14h],eax ;返回地址放入[ebp-14h] 即为p

new(a) A(); //构造
00F9152A mov eax,dword ptr [ebp-14h]
00F9152D push eax
00F9152E push 1 ;压入p
00F91530 call operator new (0F91280h);调用operator new(size_t, void* p)即placement(3)版本 只是简单返回p
00F915