深度探索c++对象模型之new和delete运算符介绍

      在c++中,无论是new还是delete,它们都被当成是运算符的,new运算符对应的是C语言中的malloc函数【在new的重载运算符函数里面调用malloc】,而delete自然对应的是C语言中的free函数了。当我们写下一句【int *p = new(7);】时,在编译转化后,最简单的版本也是这种:

//编译过后
int *p = _new( sizeof(int) ); //第一步
*p = 7; //第二步
但更多的为了安全起见,第二步的初始化操作应该在第一步内存配置成功之后再进行:

// 更先进的版本
int *p;
if( p = _new(sizeof(int)) )
  *p = 7;
delete的情况和new类似,但delete比new更需要安全,比如一个空的NULL指针,最好不要对它有任何动作,所以一般的编译器都会为【delete p;】套上一层保护膜:

if( p != 0 )
  _delete(p);

在这里需要特别注意是,【delete p;】并不是把p的指针值清零,而是把p所指向的堆上的内存资源回收释放掉,这意味着该内存地址上面的对象的生命周期已经结束,但是地址本身依然存在,所以p依然拥有一个合法的值,还能够继续使用,只不过受到了限制,被打成【void *】类型指针而已,所以像下面的代码就有一定危险性了:

if ( p != 0 ) //在delete p之后
  ...

       那么对我们的自定义类类型来说,当我们用一个new在堆上给一个类开辟出类对象时,情况差不多也是类似,不同的是要使用该类的constructor类配置该对象,比如,当我们写下如此代码:

Point *p = new Point;
时,它将会被转化为:

Point *p;
if( p = _new(sizeof(Point) )
  p = Point::Point(p);
但如果带有exception handling【异常处理】,那么转化结果要更为复杂一点:

Point *p;

if( p = _new(sizeof(Point) )
{
  try{
      p = Point::Point(p);
    }
  catch(...){
  _delete(p); //调用delete library function来释放p配置的内存
  throw; //将原来的exception上传!
  }
}
我们在堆上new出一块内存后,然后用Point的constructor配置该内存,如果发生exception,那么首先把刚才配置出来的内存回收释放掉,然后再给上一级throw过去我们捕获的exception。

      同样的道理,对于delete,类类型与内建类型还是有一点稍微的区别,毕竟多了一个析构器的调用:

//delete p;的转换
if( p != 0 )
{
  Point::~Point(p);
  _delete(p);
}
同理,我们再来看一下exception handling版下的destructor:

//delete p;的exception handling版本
if ( p != 0 )
{
  try{ Point::~Point(p); }
  catch(...){
     _delete(p);
     throw;
  }
}

      现在,让我们来看一下new的具体实现【模拟而已,不考虑exception handling】:

//有两处需要注意
extern void* operator new(size_t size)
{
  if( size ==0 )
    size = 1; //第一处,最少的也得是默认的1byte

  void *last_alloc;
  while( !(last_alloc = malloc(size)) )
  {
     if( _new_handler) //这是第二处需要注意的地方,看后续说明
       (*_new_handler)( );
     else return 0;
  }

  return last_alloc;
}

就在这里讲解一下第二处需要注意的地方吧,首先看while里面的表达式,“!( last_alloc = malloc(size) )”如果为真,意味着堆上并没有给我们开辟出可用内存资源【malloc返回null值】,这个时候就需要一些“申请内存失败”的异常处理,比如重新回到堆上回收一些无用的内存资源等等;而那个“_new_handler”其实是一个函数指针【就是处理内存申请失败的异常处理函数】,这里的意思是,可以让我们用户定义一个自己的new失败处理函数,然后把该函数地址赋值给“_new_handler”。

      那么,怎么设置我们自己的处理函数呢?可以通过“set_new_handler( void (*new_handler) () ) throw()”,它定义在new标准库中:

namespace std
{
  typedef void (*new_handler)();//表明new_handler是一个typedef,是一个既没有参数也没有返回值的函数的函数指针
  new_handler ser_new_handler(new_handler pfun) throw();
}
我们可以随便做一个简单的处理函数,比如叫“My_Handler”,然后通过set_new_handler设置一下就可以了:

int My_Handler()
{
  cout<<"对不起,内存申请失败!"<<endl;
  return 0;
}
...
...
set_new_handler(My_Handler);//在这里设置一下





发布了53 篇原创文章 · 获赞 113 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章