從operator=中“自我賦值”看new的拋出異常

在Effective C++中看到這個問題時才發現以前寫的代碼完全沒有注意過這個問題

“自我賦值”發生在對象被賦值給對象本身時,例如:

class Weight
{...};
...
w=w;

雖然這看上去確實非常的蠢,但是誰也不能保證這不會發生,畢竟這是合法的,但是問題出來了,我們寫的顯示賦值函數一般是這樣的,假設我們在類中使用了動態分配。

class Bitmap{...};
class Weight
{
...
private:
Bitmap * _pb;
};
Weight & Weight::operatot=(const Weight & rhs)
{
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

好的,現在面臨的問題是,*this和rhs這兩個指向了同一個對象,在執行delete _pb 時同時也將rhs的Bitmap銷燬了。

對此提出了兩種有效的解決方法

①傳統做法在operator=之前做一個“證同測試(identity test)”

Weight & Weight::operatot=(const Weight & rhs)
{
if(this == &rhs)
    return *this;
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

這樣做完全行得通,但是爲一件很少發生的事情花費額外的判斷操作似乎不是很划算。

②依靠精心周到的語句順序解決
調整最初的源代碼

Weight & Weight::operatot=(const Weight & rhs)
{
Bitmap *pOrig=_pb;                    //記住原先的_pb
_pb=new Bitmap(rhs._pb);              //令_pb指向*_pb的一個副本
delete pOrig;                         //刪除原先的_pb
return *this;
}

再調整順序後,在重新new之後再摧毀原來的_pb,這樣的操作具有很高的異常安全性。這裏有一個問題,如果new失敗了怎麼辦?

我相信所有人一定都寫過這樣一段代碼

int *p=new int[5];
if(p==NULL)
    return -1;

當我們使用malloc/calloc分配內存時,檢測返回值是否爲”空指針”是一個良好的習慣,可惜的是new在默認狀態下,分配失敗並不會返回一個空指針,而是拋出(throw)一個異常!!

對此正確的操作有如下兩個方法:

①捕獲異常

try
{
    _pb=new Bitmap(rhs._pb);  
    ...                     //其他操作
}
catch(const bad_alloc& e )
{
    return -1;
}

②標準 C++ 亦提供了一個方法來抑制 new 拋出異常,而返回空指針

   int* p = new (std::nothrow) int; 
   // 這樣如果 new 失敗了,就不會拋出異常,而是返回空指針
   if ( p == 0 ) // 如此這般,這個判斷就有意義了
       return -1;
   ...           // 其它代碼
   delete p;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章