深入new和delete小结

 

new和delete是c++中使用频率非常高的两个关键字,可以说C++内存操作的核心就在于这两个关键字,近几天阅读了相关的文章,发现自己对于这两个关键字的理解太过肤浅,因此做了一些个人总结。

 

首先要明确一点,new和delete所操作的内存全部是在堆区,这个区域的内存和栈区是不一样的,不会自动释放,因此一定要记得释放不使用的内存,否则会造成内存泄露。new操作有两种形式,一种是原始的new,一种是操作数组的new[],二者本质上没区别,只是操作的对象不同。对于系统内建的类型,new会直接分配定义好大小的内存区块,而对于用户自定义的类型,new会先调用构造函数,然后再分配内存区块。C++编译器会通过访问自定义类型的内部成员来计算该类型的大小,打个比方,class A里面有两个数据成员,一个是char,一个是int,那么该类对象所占的内存大小就是8+4=12字节。但是实际上,自定义类型的对象所占的内存大小是大于理论值的,原因就是C++编译器有一个cookie机制,这个机制能让自定义变量记录一些变量的全局信息,而内存大小就包括在这些信息里。这个cookie机制和web技术中的cookie其实有相似的地方,都是一种类似缓存的机制,有利于程序效率的提升。这种机制带来的一个好处就是用户在调用delete的时候再也不用指定内存的大小了,因为在调用delete的时候,编译器会自动读取cookie中分配内存的大小,从而直接释放内存,而不必像new似的,需要临时计算内存大小。而new[]的原理实际和new是一样的,只不过new[]是对数组中所有数据成员调用构造函数,然后分配内存,同理,delete[]也是对所有数据成员调用析构函数,然后释放内存。

 

对new和delete的重载具有很大意义,特别是在处理内存泄露方面。原始的new和delete虽然提供了一些处理内存异常的方法,但是在内存泄露方面基本是靠程序员自觉的,如果一个程序员忘记释放某部分内存,编译器不会提示错误。C++编译器之所以这么设计是有原因的,因为检测内存泄露所付出的代价往往是非常大的,编译器需要逐位检查数据是否置零,这对于小型数据来说也许不算什么,但如果对于集群数据来说,代价太大了,况且内存泄露的原因五花八门,这导致内存泄露在异常处理方面也会非常复杂,因此如果让new和delete支持内存泄露检测的话,它们的运行效率会下降非常多,而new和delete恰恰是c++中使用频率最高的两个操作,因此这类操作,效率肯定是放到第一位的,假如这类操作效率很低,对程序的负面影响是非常巨大的。但是这并不表示用户不可以定制自己的new和delete操作符,假如程序对内存检测的需求很高,那么用户完全可以重载这两个操作符。

 

其实new的重载远不是仅仅分配内存那么简单,在effective c++中,作者对new的重载做了比较深入的讲解,按照书中的观点,new除了分配内存,还有两个事情要做,一个是处理0字节的情况,一个是处理内存不够的情况,处理0字节的情况是把它当成1字节来处理,而处理内存不够的情况则需要借助于new_handler。new_handler是一个全局函数指针,指向一个处理内存不够的函数,这个指针通过<new>中的set_new_handler函数来设置指向的对象。如果用代码去实现的话,大概应该是这个样子:

void * operator new(size_t size)  

{

       if (size == 0) 

       {                      

              size = 1; 

       }     

 

       new_handler globalhandler = std::set_new_handler(currenthandler);

       std::cout<<"this is reloaded new"<<std::endl;

       void *memory;

       while(1)

       {

              try

              {

                     memory = ::operator new(size);

              }

              catch(std::bad_alloc)

              {

                     std::set_new_handler(globalhandler);

                     throw;

              }

              std::set_new_handler(globalhandler);

              return memory;

       }

}


 

其中currenthandler是用户自定义的内存出错处理函数,而globalhandler则是系统的全局出错处理函数。这个函数既处理了0字节情况,又处理了内存不够的情况,并且可以正常分配内存,可以说完成了new的基本功能,用户可以在这个基础上做任何扩展,都应该是没问题的。

 

而delete的重载则简单的多,由于不用指定内存大小,所以只需数据指针即可,但是,同样,delete需要处理空指针的情况,以保证delete的操作是安全的。那么实现的代码就是类似这种的:

 

void operator delete(void *rawmemory)

{

       if (rawmemory == 0) return;  

 

       ::operator delete(rawmemory); 

       return;                       

}


同样,用户可以在这个基础上做任何扩展。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值