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

作者: null122 | 来源:发表于2016-06-02 22:46 被阅读436次

    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真的复杂吗?(下)

        本文链接:https://www.haomeiwen.com/subject/lqohdttx.html