C++中的new和delete真的复杂吗?(上)中为大家介绍了与new有关的知识,这篇文章接着上一篇为大家讲解delete关键字。
前言
在上一篇文章中我们了解到new
关键字的基本功能通过operator new
与构造函数实现,而operator new
除调用malloc
分配内存外还调用new_handler
来达到对内存整理与重分配,并通过抛出异常的方式来提示内存分配错误,那delete
的执行细节又是怎样呢?我们一起来看一看!
1.delete关键字
C++ primer plus中如此描述delete关键字:
当需要内存时,可以使用new来请求。另一方面是delete运算符,它使得在使用完内存后,能够将其归还给内存池。
那么delete
到底是如何归还内存的呢?
我们先来看一下delete
简单类型时的情况:
delete a;
01061278 push eax
01061279 call dword ptr ds:[10630ACh] // delete调用了 operator delete
0106127F add esp,8
上面是delete
执行时VS Release
模式下的汇编码,有了new
的经验大家应该可以猜到call
语句的去向了。
void operator delete( void * p )
{
RTCCALLBACK(_RTC_Free_hook, (p, 0));
free( p );
}
继续跟踪我们发现与operator new
相比较operator delete
相当的简洁,这里有一个RTCCALLBACK
的宏定义,其原型我们可以在 rtcsup.h
找到:
# define RTCCALLBACK(a, b)
很明显他是一个空的宏定义,这就意味着对于简单类型operator delete
只是简单的调用了free
。
如果读者在VS2010
之后版本的编译器中使用delete时可能会发现delete
后指针指向的地址发生了变化,这是因为在VS2010
及之后的版本中加入了叫做safe delete
特性,将delete
后的指针指向特定安全值防止使用野指针或重复delete
。如果用户使用delete
后的指针,或重复delete
一个指针,程序会抛出异常。
注意safe delete功能可以通过将指针赋NULL来实现但微软并没有这样做,因为delete一个空指针在C++中是
合法的,并不会抛出异常。在VS中delete一个空指针也不会触发safe delete。
2.delete复杂类型
class MyObject
{
public:
int a;
MyObject()
{
a = 1;
}
~MyObject()
{
a = 0;
}
};
delete在DEBUG模式下的汇编码。
MyObject::`scalar deleting destructor':
003E2800 push ebp
003E2801 mov ebp,esp
003E2803 sub esp,0CCh
003E2809 push ebx
003E280A push esi
003E280B push edi
003E280C push ecx
003E280D lea edi,[ebp-0CCh]
003E2813 mov ecx,33h
003E2818 mov eax,0CCCCCCCCh
003E281D rep stos dword ptr es:[edi]
003E281F pop ecx
003E2820 mov dword ptr [this],ecx
003E2823 mov ecx,dword ptr [this]
003E2826 call MyObject::~MyObject (03E1424h) //先调用析构函数
003E282B mov eax,dword ptr [ebp+8]
003E282E and eax,1
003E2831 je MyObject::`scalar deleting destructor'+3Fh (03E283Fh)
003E2833 mov eax,dword ptr [this]
003E2836 push eax
003E2837 call operator delete (03E1122h) // 后调用operator delete
从上面汇编执行过程我们就可以看出,delete
在复杂类型情况下执行与new
相反,先调用析构函数,在执行operator delete
归还内存。
3.delete的执行过程:
相对于new
运算符delete
的执行过程可以说相当简明:
delete -> 析构函数(如果有) -> operator delete -> RTCCALLBACK空宏定义 -> free
4.重载operator delete
和operator new
一样,我们也可以重载operator delete
。
class MyObject
{
public:
int a;
MyObject()
{
a = 1;
}
~MyObject()
{
a = 0;
}
void operator delete(void *p)
{
std::cout << "delete MyObject" << std::endl;
return ::operator delete(p);
}
};
这里要注意由于operator new和operator delete 都是静态函数,如过将其重载为非Public
类型将导致无法使用new
和delete
,当然也可以使用这一特性构造出特殊的代码。
5.delete[]
delete[] m;
00B4393D mov eax,dword ptr [m]
00B43940 mov dword ptr [ebp-0E0h],eax
00B43946 mov ecx,dword ptr [ebp-0E0h]
00B4394C mov dword ptr [ebp-0ECh],ecx
00B43952 cmp dword ptr [ebp-0ECh],0
00B43959 je main+0F0h (0B43970h)
00B4395B push 3
00B4395D mov ecx,dword ptr [ebp-0ECh]
00B43963 call MyObject::`vector deleting destructor' (0B414B0h)
//delete[] 使用 vector deleting destructor 来释放数组
delete[]
使用vector deleting destructor
来释放数组,而复杂类型使用数组头指针储存数组长度,使用delete[]没有问题,但使用delete
就变成了简单释放头指针指向的内存这会造成内存泄露。
而简单数据类型则完全没有问题,也就是说:
int * a = new int[15];
delete a;
这是可行的,具体为什么可行就要到free
内部找答案了。读者可自行探究。虽然可行,但从习惯上来说还是建议使用delete[]
释放 int[]
申请的内存。
new
与delete
还有很多重载用法,这里就不再向大家一一列举了,希望大家喜欢我的文章!
●null122转载请注明出处