問題聚焦:
使用了資源管理對象(如智能指針),就一定是安全的嗎?顯然不是。
資源泄露發生可能在於,在“資源被創建”和“資源被轉換爲資源管理對象”兩個時間點之間有可能發生異常干擾。
看下面這個例子:
//函數說明
int priority(); //揭示處理程序的優先權
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority); // 用來在某動態分配所得的Widget上進行某些帶有優先權的處理
// 這裏對動態分配得來的Widget運用智能指針
// 現在考慮調用processWidget
processWidget(new Widget, priority()); // 顯然編譯是不通過的,原因是類型不匹配。
// 如果像下面這樣調用即可
processWidget(std::tr1::shared_prt<Widget>(new Widget), priority()); // 編譯通過
然而令人驚訝的是,上述正確的調用方法,卻可能發生資源泄露。
分析:
調用processWidget之前,編譯器需要做三件事:
- 調用priority
- 執行“new Widget”
- 調用tr1:shared_ptr構造函數
問題在於:C++編譯器是以什麼樣的次序完成這些事情的呢?
答案是不確定的。
可以確定的是:“new Widget”一定執行於tr1::shared_ptr構造函數被調用之前。
但是對priority的調用則可以排在第一第二或第三的位置。
當priority的調用排在第二的位置時,執行次序如下:
- 執行“new Widget”
- 調用priority
- 調用tr1:shared_ptr構造函數
潛在的問題:如果第二步發生異常,new Widget返回的指針將會遺失。(其實爲什麼會遺失,這個問題有待驗證)
所以,資源泄露發生可能在於,在“資源被創建”和“資源被轉換爲資源管理對象”兩個時間點之間有可能發生異常干擾。
解決的方案:
使用分離語句
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
依據在於:編譯器對於“跨越語句的各項操作”沒有重新排列的自由。
小結:
以獨立語句將newed對象存儲於智能指針內,如果不這樣做,一旦異常被拋出,有可能導致難以察覺的資源泄漏。
參考資料:
《Effective C++ 3rd》