深度探索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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章