你不知道的c++之new与delete和new []与delete[]
有如下代码
反汇编如下
查看代码发现并没有什么不同
因为int 类型在c++中 不是对象,在构造时不需要调用构造函数,销毁时不用调用析构函数,所以直接分配,回收空间就行了
修改代码如下
反汇编如下
看到区别了?
发现编译器在遇到非内置对象使用new和new[]与内置对象使用new与new[]做了不同处理
而且在对非内置对象使用new与new[]时也做了不同的处理
new[]并没有“call @ILT+65(father::father)”而是“call `eh vector constructor iterator'” ,那么在“call `eh vector constructor iterator'“时发生了什么?
首先我们根据反汇编代码来画出此调用时main函数的栈空间
然后查看此调用的反汇编代码
首先我们从画红线的操作开始分析
mov ecx,dword ptr [ebp+8] 把dword ptr [ebp+8]单元的值放入ecx寄存器
call dword ptr [ebp+14h] 调用dword ptr [ebp+14h]单元存放的函数
我们查看栈帧可看到dword ptr [ebp+8]存放的是q+4, dword ptr [ebp+14h]存放的是构造函数father()的地址
现在你知道这两个操作的做用是什么了?
第一个操作为构造函数传入this指针(q+4)做参数,第二个操作调用构造函数
至于为什么会传入q+4而不是q做参数等会再分析
下面让我们来看接下来的3个操作
mov edx,dword ptr [ebp+8]
add edx,dword ptr [ebp+0Ch]
mov dword ptr [ebp+8],edx
这三个操作就是把dword ptr [ebp+8]单元的值与dword ptr [ebp+0Ch]单元的值相加再存入dword ptr [ebp+8]单元(this+4做为下次调用构造函数的this参数)
查看栈帧会看到dword ptr [ebp+0Ch]单元存放的是4,4代表什么?
4代表刚刚被构造对象的大小,
接着的操作执行了jmp操作,查看跳转地址发现跳到了
0040EEFA mov eax,dword ptr [ebp-1Ch]此处
来看下接下来的5个操作和此操作是在做什么?
mov eax,dword ptr [ebp-1Ch]
add eax,1
mov dword ptr [ebp-1Ch],eax
mov ecx,dword ptr [ebp-1Ch]
cmp ecx,dword ptr [ebp+10h]
jge `eh vector constructor iterator'+5Ch (0040ef1c)
这6个操作就是把dword ptr [ebp-1Ch]单元的值加一再放回去,然后与dword ptr [ebp+10h]单元的值比较如果大于等于此单元的值代表已经完成了对象构造
查看栈帧发现dword ptr [ebp+10h]单元的值是3,3代表什么?数组的大小
查看代码发现dword ptr [ebp-1Ch]单元的值是0,
所以此6个操作的作用就是保证所有数组元素都能完成构造
下面让我们来分析构造第一个对象时为什么会为构造函数传入q+4做this指针,要解决这个问题我们要回到使用new[]操作符时
查看反汇编代码会发现
编译器为第二个operator new()传入的参数是16而不是12,多出的4个字节是干什么的?
继续查看红线标识的操作你会发现数组的大小3被放到了operator new()所分配的空间的前四个字节中,现在你知道为什么在构造第一个对象时会传入q+4做this指针了?
因为q指向的是3,q+4指向的才是为第一个对象分配的空间
然而但是你会发现好像在对象空间存放个3,并没有什么鸟用,到现在为止根本没用到这个位置的3,一直用的都是位于栈中的3。
下面该delete[]上场了
当你调用delete[]时那栈中的3还会存在???
然而查看代码又让你失望了,你发现在delete p时编译器生成了一条push 1指令,在delete[] q时编译器生成了一条push 3指令,既然在这编译器已经给出了要析构对象的个数,那对象空间中前四个字节存放的那个3又有何用?
下面来查看father::`vector deleting destructor'的反汇编代码
分析红线标识的操作,dword ptr[ebp+8]就是刚刚被压栈的3,把3和2 比较这有什么用?
为什么是3与2比较而不是和其它的数字比较?
如果数组的大小为2 ,那么将会执行不一样的操作,这有点说不过去呀,为什么会对2做特殊对待???
还有为什么delete p时会push 1做参数??
继续查看father::`scalar deleting destructor'
你会发现编译器会让1与1比较,这是为什么?
1,2,3你不觉得有点奇怪?我觉得有点,于是我修改代码如下
查看反汇编代码
会发现编译器还是会生成push 3,而不是你认为的push 5,原来这只是个巧合。
哈哈现在那对象空间中的前四个字节有用了?
现在我们来看下那四个字节到底有没有被使用到,继续查看father::`vector deleting destructor'的反汇编代码
Mov edx, dword ptr[ecx-4]就是把那四个字节放入edx寄存器中,你可能会问ecx中的值不是this(第一个对象)?那这里的ecx-4(this-4)是什么意思?
下面让我们查看一下father *q=new father[3
]的代码
红线标识的操作就是把operator new()返回的指针向前移动4个字节指向对象1,这就是为什么会使用this-4做地址去取得3
通过上面的分析可以看到在c++中new与delete,new[]与delete[]要配对使用
注:vc++6.0