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

2014-11-23 23:33:58 · 作者: · 浏览: 30
35 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 从反汇编中可以看出,其实operator new调用了两次,只不过每一次调用不同的重载函数,并且placement new的主要作用只是将p放入ecx,并且调用其构造函数。
事实上,在指定地址上构造对象还有另一种方法,即手动调用构造函数:p->A::A(); 这里要加上A::作用域,否则编译器会报错:
error C2273: “函数样式转换”: 位于“->”运算符右边时非法
用p->A::A();替换掉new(p) A();仍然能达到同样的效果,反汇编:
[plain]
A* a = (A*)::operator new(sizeof(A)); //分配
010614FE push 1
01061500 call operator new (1061208h)
01061505 add esp,4
01061508 mov dword ptr [a],eax

//new(a) A(); //构造
a->A::A();
0106150B mov ecx,dword ptr [a]
0106150E call operator new (1061285h)

a->~A(); //析构
01061513 push 0
01061515 mov ecx,dword ptr [a]
01061518 call A::`scalar deleting destructor' (1061041h)

::operator delete(a); //释放
0106151D mov eax,dword ptr [a]
01061520 push eax
01061521 call operator delete (10610B9h)
01061526 add esp,4

A* a = (A*)::operator new(sizeof(A)); //分配
010614FE push 1
01061500 call operator new (1061208h)
01061505 add esp,4
01061508 mov dword ptr [a],eax

//new(a) A(); //构造
a->A::A();
0106150B mov ecx,dword ptr [a]
0106150E call operator new (1061285h)

a->~A(); //析构
01061513 push 0
01061515 mov ecx,dword ptr [a]
01061518 call A::`scalar deleting destructor' (1061041h)

::operator delete(a); //释放
0106151D mov eax,dword ptr [a]
01061520 push eax
01061521 call operator delete (10610B9h)
01061526 add esp,4 比之前的方法更加简洁高效(不需要调用placement new)。不知道手动调用构造函数是否有违C++标准或有什么隐晦,我在其他很多有名的内存池(包括SGI STL alloc)实现上看到都是用的placement new,而不是手动调用构造函数。


三 operator new重载:
前面简单提到过 A* p = new A;所发生的事情:先调用operator new,如果类A重载了operator new,那么就使用该重载版本,否则使用全局版本::operatro new(size_t size)。那么类中可以重载operator new的哪些版本?全局operator new可以重载吗?全局和类中重载分别会在什么时机调用?
1.在类中重载operator new
上面提到的throwing(1)和nothrow(2)的operator new是可以被重载的,比如:
[cpp]
#include
class A
{
public:
A()
{
std::cout<<"call A constructor"< }

~A()
{
std::cout<<"call A destructor"< }
void* operator new(size