浅谈new、delete

首先我们你知道C语言中使用malloc、calloc、realloc和free进行动态内存管理。
malloc、calloc、realloc用来在堆上开辟空间, free将申 请的空间释放掉。

在C++中这些都还是可以用的,但是我们用到更多的还是new和delete,new[]和delete[];
注意在这里两个组合一定要匹配使用。

一、new、delete.
1.new
首先我们随便写一个代码
int* p=new int
然后我们看他的反汇编
这里写图片描述
分析它先给我们压栈4byte的参数后再调用operator new。源代码如下:
注意:这里的operator new和operator函数不同(比如operator =),这函数没有重载.

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                        _THROW_NCEE(_XSTD bad_alloc, );
                }

        return (p);
        }

分析operator new源代码:如果malloc调用失败,也就是内存申请失败就会调用_callnewh()这个函数
这个callnewh()是什么呢?它是一个new handler,通俗来讲就是new失败的时候调用的回调函数。

namespace std {

#ifdef _M_CEE_PURE
typedef void (__clrcall * new_handler) ();
#else  /* _M_CEE_PURE */
typedef void (__cdecl * new_handler) ();
#endif  /* _M_CEE_PURE */
#ifdef _M_CEE
typedef void (__clrcall * _new_handler_m) ();
#endif  /* _M_CEE */
_CRTIMP2 new_handler __cdecl set_new_handler(_In_opt_ new_handler _NewHandler) throw();
};

这里:
1、调用一个客户指定的错误处理函数——一个new-handler。
2、抛出异常bad_alloc(【旧】返回一个null指针)。
可以通过_set_new_handler来设置。
客户定义的错误处理函数new handler应该什么样子?

void My_NewHandler()//没有参数也不返回任何值的函数
{
    std::cerr << "Unable to satisfy request for memory\n";//提示
    //1让更多内存可用——程序开始执行就分配一块内存,当new-handler第一次被调用,将它们释还给程序使用。
    //2调用std::set_new_handler呼叫另一个new_handler函数。或者直接修改自身,比如改static数据,全局数据,namespace数据。
    //3抛出bad_alloc异常(或派生),异常不会被operatornew捕捉,会被传播到内存索求处。
    //4不返回,调用abort或者exit终止程序。
    std::abort();//终止程序
}
void main()  
{   
    _set_new_handler(My_NewHandler);  

    while (1)  
    {  
        int* p = new int[10000000];  
    }  
}  

另外的如果在类中,new还会调用构造函数,而malloc就不会调用构造函数
这里写图片描述

这里写图片描述
相同的delete p会调用析构函数,而free不调用.
总结:
{
1.调用operator new分配空间。
调用构造函数初始化对象。
2.new和delete他们只 负责分配空间/释放空间, 不会调用对象构造函数/析构函数来初始化/清理对象。
3.实际operator new和operator delete只是malloc和free的一层封装。
4.new分配失败的时候不像malloc那样返回NULL,它直接抛出异常。要判断是否分配成功应该用异常捕获的机制;
}

2.delete.
先来看反汇编,和new原理几乎相同
这里写图片描述
这里写图片描述
调用析构函数清理对象
再调用operator delete释放空间
下面是operator delete实现代码:

void operator delete( void * p )
{
    RTCCALLBACK(_RTC_Free_hook, (p, 0));

    free( p );
}

RTCCALLBACK默认是空的宏定义,所以这个函数默认情况下就是简单的调用free函数。
总结:
delete简单数据类型默认只是调用free函数。
总结步骤图:
这里写图片描述
二new[]、delete[]。
1.new[](类的数组)
首先我们可以看反汇编
这里写图片描述
然后我们F11进去
这里写图片描述
再F11
这里写图片描述
这时我们可以看到和new一样最后还是调用的operator new。
再看constructor 这个进去之后是调用构造函数.
这里写图片描述
仔细看这里的汇编它先给我们push进去一个 2Ch 2Ch是多少呢?是44
我们再来看看这个图
这里写图片描述
很明显这里的count是编译器给我们申请的字节数,如果你打开监视,你会发现这里的count就是44
这里的ecx就是申请到的空间地址
我们可以看到其中push 0Ah=10;
打开内存窗口可以看到,p地址的上一个地址正好是a也就是10;
这里写图片描述
这就证明,给ecx+4,也就是ecx指针偏移四个字节,最后将偏移后ecx给p,这里才是用户拿到的地址.
这里就有很多疑惑,我们只需要10*4=40个字节为什么要给我们多申请四个字节呢?
为什么要给申请到的地址先压栈一个10,然后再把偏移四个字节之后地址的给用户使用呢。对类类型,

delete一个数组时(比如,delete []sa;),要为每一个数组元素调用析构函数。但对于delete表达式(比如,这里的delete []p,它并不知道数组的元素个数(只有new函数和delete函数知道)。因此,必须有一种手段来告诉delete表达式的数组大小是多少。那么一种可行的方式就是,多分配一个大小为4字节的空间来记录数组大小,并可以约定前四字节来记录大小。那么,由new函数分配的地址与new表达式返回的地址应该相差4个字节(这可以写程序来验证)。对于非类类型数组和不需要调用析构函数的类类型数组,这多于的四字节就不需要了。

总结:
调用operator new分配空间。
调用N次构造函数分别初始化每个对象。
多申请四个字节用来存放数组大小
2。delete[]
这里就不分析delete[]了和前面的分析方法差不多可以自己动手看看反汇编。
这里写图片描述
/*****/
一个进击的学生
/****/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值