国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++箴言:在operator= 中處理自賦值

2019-11-17 05:09:45
字體:
來源:轉載
供稿:網友

  一個對象賦值給自己的時候就發生了一次自賦值:

class Widget { ... };

Widget w;
...

w = w; // assignment to self
  這看起來很愚蠢,但它是合法的,所以應該確信客戶會這樣做。另外,賦值并不總是那么輕易辨別。例如,

a[i] = a[j]; // potential assignment to self
  假如 i 和 j 有同樣的值就是一個自賦值,還有

*px = *py; // potential assignment to self
  假如 px 和 py 碰巧指向同一個東西,這也是一個自賦值。這些很不明顯的自賦值是由別名(有不止一個方法引用一個對象)造成的。通常,當引用或者指針指向相同類型的多個對象時,對這些引用和指針進行操作的代碼需要考慮到那些對象可能是相同的。實際上,假如兩個對象來自同一個繼續體系,甚至不需要公開聲明,它們就是相同類型的,因為一個基類的引用或者指針也能夠引用或者指向派生類類型的對象:

class Base { ... };

class Derived: public Base { ... };

void doSomething(const Base& rb, // rb and *pd might actually be
Derived* pd); // the same object
  假如你遵循 Item 13 和14 的建議,你應該總是使用對象來治理資源,而且你應該確保那些資源治理對象(resource-managing objects)被拷貝時行為良好。假如是這種情況,你的賦值運算符在你沒有考慮自拷貝的時候可能也是自賦值安全(self-assignment-safe)的。但是,假如你試圖自己治理資源,無論如何(假如你寫一個資源治理類(resource-managing class),你當然不得不做),你可能會落入在你使用完一個資源之前就已意外地將它釋放的陷阱。例如,假設你創建了一個類,它持有一個指向動態分配 bitmap 的指針:

class Bitmap { ... };

class Widget {
 ...
 PRivate:
  Bitmap *pb; // ptr to a heap-allocated object
};
  下面是一個 Operator= 的實現,表面上看它是合理的,但假如出現自賦值則是不安全的。(它也不是異常安全(exception-safe)的,但我們要過一會兒才會涉及到它。)

Widget&
Widget::operator=(const Widget& rhs) // unsafe impl. of operator=
{
 delete pb; // stop using current bitmap
 pb = new Bitmap(*rhs.pb); // start using a copy of rhs’s bitmap

 return *this; // see Item 10
}
  這里的自賦值問題在 operator= 的內部,*this(賦值的目標)和 rhs 不能是同一個對象。假如它們是,則那個 delete 不僅會銷毀當前對象的 bitmap,也會銷毀 rhs 的 bitmap。在函數的結尾,Widget——通過自賦值應該沒有變化——發現自己持有一個指向已刪除對象的指針。

  防止這個錯誤的傳統方法是在 operator= 的開始處通過一致性檢測來阻止自賦值:

Widget& Widget::operator=(const Widget& rhs)
{
 if (this == &rhs) return *this; // identity test: if a self-assignment,
 // do nothing
 delete pb;
 pb = new Bitmap(*rhs.pb);

 return *this;
}
  這個也能工作,但是我在前面提及那個先前版本的 operator= 不僅僅是自賦值不安全(self-assignment-unsafe),它也是異常不安全(exception-unsafe)的,而且這個版本還是有異常上的麻煩。具體地說,假如 "new Bitmap" 表達式引發一個異常(可能因為供分配的內存不足或者因為 Bitmap 的拷貝構造函數拋出一個異常),Widget 將以持有一個被刪除的 Bitmap 的指針而告終。這樣的指針是有毒的,你不能安全地刪除它們。你甚至不能安全地讀取它們。你對它們唯一能做的安全的事情就是花費大量的調試精力來斷定它們起因于哪里。

  碰巧的是,使 operator= 異常安全(exception-safe)也同時彌補了它的自賦值安全(self-assignment-safe)。這就導致了更加通用的處理自賦值問題的方法就是忽略它,而將焦點集中于取得異常安全。Item 29 更加深入地探討了異常安全,但是在本 Item 中,已經足夠我們在很多情況下來觀察它,仔細地調整一下語句的順序就可以得到異常安全(exception-safe)(同時也是自賦值安全)的代碼。例如,在這里,我們只要注重直到我們拷貝了 pb 指向的目標之后,才能刪除它:

Widget& Widget::operator=(const Widget& rhs)
{
 Bitmap *pOrig = pb; // remember original pb
 pb = new Bitmap(*rhs.pb); // make pb point to a copy of *pb
 delete pOrig; // delete the original pb

 return *this;
}
  現在,假如 "new Bitmap" 拋出一個異常,pb(以及它所在的 Widget)的遺跡沒有被改變。甚至不需要一致性檢測,代碼也能處理自賦值,因為我們為原先的 bitmap 做了一個拷貝,刪除原先的 bitmap,然后指向我們作成的拷貝。這可能不是處理自賦值的最有效率的做法,但它能夠工作。

  假如你關心效率,你可以在函數開始處加上一致性檢測。在這樣做之前,無論如何,先問一下自己,你認為自賦值發生的頻率有多大,因為檢測并不是免費午餐。他將使代碼(源代碼和目標代碼)有少量增大,而且他將在順序流程中引入一個分支,這兩點會減慢運行速度。例如,指令預讀取(instrUCtion prefetching),高速緩存(caching)和流水線操作(pipelining)的效力都將被降低。

  在 operator= 中另一個可選擇的,確保實現是異常安全和自賦值安全的,手動排序語句的技術被稱為 "copy and swap"。這一技術和異常安全關系密切,所以將在 Item 29 中描述。無論如何,這是一個寫 operator= 的足夠通用的方法,值得一看,這樣一個實現看起來通常就像下面這樣:


class Widget {
 ... 
 void swap(Widget& rhs); // exchange *this’s and rhs’s data;
 ... // see Item 29 for details
};

Widget& Widget::operator=(const Widget& rhs)
{
 Widget temp(rhs); // make a copy of rhs’s data

 swap(temp); // swap *this’s data with the copy’s
 return *this;
}
  在這個主題上的一個變種利用了如下事實:(1)一個類的拷貝賦值運算符可以被聲明為傳值參數;(2)通過傳遞一些東西的值來做出它的拷貝:

Widget& Widget::operator=(Widget rhs) // rhs is a copy of the object
{ // passed in - note pass by val

 swap(rhs); // swap *this’s data with
 // the copy’s
 return *this;
}
  對我本人來說,我擔心這個方法會攪亂一些聰明的頭腦,但是通過將拷貝操作從函數體中移到參數的構造中,它有時確實能使編譯器產生更有效率的代碼。

  Things to Remember

  ·當一個對象自賦值的時候,確保 operator= 能行為良好。技巧包括比較源對象和目標對象的地址,關注語句順序,和 copy-and-swap。

  ·假如兩個或更多對象相同,確保任何操作多于一個對象的函數行為正確。 更多文章 更多內容請看C/C++技術專題  數據庫處理專題專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 江都市| 北流市| 重庆市| 荣成市| 马龙县| 长治县| 静安区| 赣州市| 图们市| 铜川市| 新源县| 全椒县| 鄂伦春自治旗| 通州区| 黄骅市| 中卫市| 南乐县| 牡丹江市| 宣城市| 瑞丽市| 龙南县| 封丘县| 大埔区| 禹州市| 泉州市| 鄢陵县| 新沂市| 临海市| 江陵县| 泸州市| 始兴县| 阳谷县| 新乡县| 姚安县| 松滋市| 平谷区| 尼玛县| 乌兰县| 巢湖市| 涿州市| 阜城县|