Guru of the Week 條款22:物件的生存期(第一部分) (轉)

worldblog發表於2007-12-11
Guru of the Week 條款22:物件的生存期(第一部分) (轉)[@more@]

GotW #22 Lifetimes – Part I:namespace prefix = o ns = "urn:schemas--com::office" />

著者:Herb Sutter

翻譯:K ][ N G of @rk™

[宣告]:本文內容取自網站上的Guru of the Week欄目,其著作權歸原著者本人所有。譯者kingofark在未經原著者本人同意的情況下翻譯本文。本翻譯內容僅供自學和參考用,請所有閱讀過本文的人不要擅自轉載、傳播本翻譯內容;本翻譯內容的人請在閱讀瀏覽後,立即刪除其。譯者kingofark對違反上述兩條原則的人不負任何責任。特此宣告。

Revision 1.0

 

Guru of the Week 條款22:的生存期(第一部分)

 

難度:5 / 10

 

(“生存,還是滅亡……[譯註:這是莎士比亞所著《哈姆雷特》中的名句]” 一個物件何時才算是真實存在的?這個問題用來考察一個物件何時才能被的使用。)

 

[Problem]

[問題]

 

評述下面的段。#2處的程式碼使安全和/或合法的嗎?請對你的回答做出解釋。

 

  void f() {


  T t(1);


  T& rt = t;


  // #1: 使用t或者rt做一些事情


  t.~T();


  new (&t) T(2);


  // #2: 使用t或者rt做一些事情


  } // t被再次銷燬


 

 [解答]

 

是的,#2處的程式碼是安全且合法的(如果只考慮這部分程式碼的話),但:

a)  作為一個整體,它是不安全的,而且

b)  這樣做是一個壞習慣。

 

 [為什麼#2是安全的(如果只考慮這部分程式碼的話)?]

 

C++標準草案明確規定,允許這種程式碼出現。現場的析構和重構造(in-place destruction and reconstruction)不會使rt這個引用失效。(當然,你不能在t.~T()與placement new之間使用t或rt,因為在那段時期裡不存在任何物件。我們還假設T::operator&()沒有被過載,即沒有被用來做「返回物件之地址」以外的其它事情。)

 

我們之所以說“如果只考慮這部分程式碼的話,#2就是安全的”,是因為f()作為一個整體而言,可能不是異常安全的(exception-safe):

 

 [為什麼函式是不安全的?]

 

如果在T(2)的時候,T的建構函式有丟擲異常的可能,那麼f()就不是異常安全的。考慮其原因:如果T(2)丟擲異常,那麼在原來’t’所在的區域中將不會有新的物件被構造,而在函式末尾T::~T()仍然被正常呼叫(因為t是一個自動變數[automatic variable]),而且正如程式碼中的註釋所述,“t被再次銷燬”。這即是說,’t’會被構造一次,卻被銷燬兩次(嗚呼呀)。這將導致容易產生無法預見的副作用,比如core dumps。

 

 [為什麼這是個壞習慣?]

 

如果忽略異常安全性的問題,那麼程式碼在這樣的設定下恰好就能夠正常工作,這是因為程式設計師此時知道被構造和銷燬之物件的具體型別。這即是說,該物件是一個T,並被作為一個T來被銷燬和重新構造。

 

在實際的程式碼中,這種技術(即便真是編碼所需)幾乎不會被使用,並且這樣做也是非常壞的習慣;原因是:如果其出現在成員函式中,那麼其將會充滿(有時難以捉摸的)危險:

 

  void T::f( int i ) {


  this->~T();


  new (this) T(i);


  }


 

現在這種技術還算安全嗎?基本上來說,不安全。考慮下面的程式碼:

 

  class U : /*...*/ public T { /* ... */ }; 


  void f() {


  /*AAA*/ t(1);


  /*BBB*/& rt = t;


  // #1: 使用t或者rt做一些事情


  t.f(2);


  // #2: 使用t或者rt做一些事情


  } // t被再次銷燬


 

如果”/*AAA*/”是”T”,那麼#2處的程式碼仍然可行,即使”/*BBB*/”不是”T”( ”/*BBB*/”可能是T的基類)。

 

如果”/*AAA*/”是”U”(譯註:而不是”T”),那麼無論”/*BBB*/”是什麼,都已經毫無懸念了。大概你所能期待的最好結果就是一個及時的core dump,因為對t.f()的呼叫將物件“切割(slices)”了。這裡說的“切割”是指:t.f()用屬於另一個不同型別的物件替換了原來的物件——這即是說函式使用了T而不是U。即便是你意欲編寫不可移植的程式碼,你也無法知曉「當原來U所在的記憶體區域被T物件之資料抹蓋以後,其被作為U是否還可用?」。固然還是有情況尚佳的機率,但是請不要走到那個地步……這絕不是一次良好的實踐。

 

本期GotW包含了一些基本的、有關現場析構和重構(in-place destruction and reconstruction)的安全性問題和切割問題。這為下期的“GotW條款23:物件生存期(第二部分)”作下鋪墊。

(完)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991265/,如需轉載,請註明出處,否則將追究法律責任。

相關文章