[轉(zhuǎn)]http://www.shnenglu.com/tiandejian/archive/2007/05/15/ec_17.html
第17條: 要在單獨的語句中使用智能指針來存儲由 new 創(chuàng)建的對象
假設這里有一個函數(shù)用來顯示處理優(yōu)先級,另一個函數(shù)根據(jù)當前優(yōu)先級為一個動態(tài)分配的 Widget 做一些處理:
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
一定要時刻記住“使用對象管理資源”這一真理(參見第 13 條)。 processWidget 中可以使用智能指針來動態(tài)分配其需要處理的 Widget 。
下面是對 progressWidget 的一次調(diào)用:
processWidget(new Widget, priority());
請稍等,不要試圖這樣調(diào)用。這將不會通過編譯。 tr1::shared_ptr 的構造函數(shù)中包含了一個 explicit 的裸指針,于是便不存在從“ new Widget ”語句返回的裸指針到 processWidget 所需的 tr1::shared_ptr 的隱式轉(zhuǎn)換。然而下邊的代碼將順利通過編譯:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
看上去有些令人吃驚,盡管我們時時刻刻使用對象來管理資源,但是這里還是有出現(xiàn)資源泄漏的可能。了解這些發(fā)生的原由對我們深入理解是有一定幫助的。
在編譯器能夠生成對 processWidget 的調(diào)用之前,它必須評估傳入的參數(shù)。第二個參數(shù)僅僅調(diào)用了一個函數(shù) priority ,但是第一個參數(shù)(“ std::tr1::shared_ptr<Widget>(new Widget) ”)包含兩部分:
l 運行 “new Widget” 語句
l 調(diào)用 tr1::shared_ptr 的構造函數(shù)
因此,在 processWidget 可以被調(diào)用之前,編譯器必須自動生成代碼來解決下面的三件事情:
l 調(diào)用 priority 。
l 執(zhí)行 “new Widget” 。
l 調(diào)用 tr1::shared_ptr 的構造函數(shù)。
C++ 編譯器對于這三項任務完成的順序要求得很寬松。(這一點與 Java 和 C# 就很不一樣了,這兩門語言中的函數(shù)參數(shù)總是以一個特定的順序得到評估。)由于“ new Widget ”語句為 tr1::shared_ptr 的構造函數(shù)傳遞了一個參數(shù),因此它必須在 tr1::shared_ptr 的構造函數(shù)被調(diào)用之前得到執(zhí)行。但是調(diào)用 priority 的工作可以放到第一,第二,也可以放在最后。如果編譯器決定第二個處理它(這樣可以使代碼更高效),我們就會得到這樣的執(zhí)行序列:
1. 執(zhí)行 “ new Widget ” .
2. 調(diào)用 priority 。
3. 調(diào)用 tr1::shared_ptr 的構造函數(shù)。
但是請想象一下如果調(diào)用 priority 時拋出了一個異常的話,將會發(fā)生些什么。在這種情況下,由于“ new Widget ”返回的指針不會如我們所愿保存在 tr1::shared_ptr 中,因此它很有可能會丟失,于是內(nèi)存泄漏就發(fā)生了。在資源被創(chuàng)建以后和這個資源轉(zhuǎn)交給一個資源管理對象之前的這段時間內(nèi),有可能發(fā)生異常,如果發(fā)生的話,那么調(diào)用 processWidget 就會造成資源泄漏。
防止這類問題發(fā)生的辦法很簡單:使用單獨的語句,創(chuàng)建 Widget 并將其存入一個智能指針,然后將這個智能指針傳遞給 processWidget :
std::tr1::shared_ptr<Widget> pw(new Widget);
// 在一個單獨的語句中創(chuàng)建 Widget
// 并存入一個智能指針
processWidget(pw, priority()); // 這樣調(diào)用就不會泄漏了。
這樣是可行的,因為編譯器為多行的語句安排執(zhí)行順序要比單一的語句時嚴格得多。由于這段改進的代碼中,“ new Widget ”語句以及對 tr1::shared_ptr 的構造函數(shù)的調(diào)用在單獨的語句中,對 priority 的調(diào)用在另一個單獨的語句中,所以編譯器就沒有機會調(diào)換處理順序了。
牢記在心
l 在單獨的語句中使用智能指針來保存由 new 創(chuàng)建的對象。如果不這樣做,你的程序會在拋出異常時發(fā)生資源泄漏。