C++中的new和delete真的复杂吗?(下)

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类型将导致无法使用newdelete,当然也可以使用这一特性构造出特殊的代码。

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[]申请的内存。

newdelete还有很多重载用法,这里就不再向大家一一列举了,希望大家喜欢我的文章!
            ●null122转载请注明出处

你可能感兴趣的:(C++中的new和delete真的复杂吗?(下))