Item 17: 在 standalone statements(獨立語句)中將 new 出來的 objects(對象)存入 smart pointers(智能指標)
作者:Scott Meyers
譯者:fatalerror99 (iTePub's Nirvana)
發布:http://blog.csdn.net/fatalerror99/
假設我們有一個函數用於取得我們的 processing priority(處理優先順序),還有第二個函數用於根據 priority(優先順序)對一個 dynamically allocated(動態分配)的 Widget 做一些處理:
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
不要忘記 using objects to manage resources(使用對象管理資源)的至理名言(參見 Item 13),processWidget 為 dynamically allocated(動態分配)的 Widget 使用了一個 smart pointer(智能指標)(在此,是一個 tr1::shared_ptr)。
現在考慮一個對 processWidget 的調用:
processWidget(new Widget, priority());
且慢,別想這樣調用。它不能編譯。tr1::shared_ptr 的取得一個 raw pointer(裸指標)的 constructor(建構函式)是 explicit(顯式)的,所以沒有從運算式 "new Widget" 返回的 raw pointer(裸指標)到 processWidget 所需的 tr1::shared_ptr 之間的隱式轉換。然而,下面的代碼,是可以編譯的:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
令人驚訝的是,儘管我們這裡到處都在使用 object-managing resources(由對象管理的資源),這個調用還是可能泄漏資源。看看這是如何發生的會得到一些啟示。
在編譯器可以產生一個對 processWidget 的調用之前,它們必須計算被作為 parameters(形參)傳遞的 arguments(實參)的值。第二個 arguments(實參)不過是對函數 priority 的一個調用,但是第一個 arguments(實參) ("std::tr1::shared_ptr<Widget>(new Widget)"),由兩部分組成:
- 運算式 "new Widget" 的執行。
- 對 tr1::shared_ptr constructor(建構函式)的一個調用。
因此,在 processWidget 可以被調用之前,編譯器必鬚生成代碼來做這三件事:
- 調用 priority。
- 執行 "new Widget"。
- 調用 tr1::shared_ptr constructor(建構函式)。
C++ 編譯器允許在一個相當大的範圍內決定這三件事被完成的順序。(這裡與 Java 和 C# 等語言的處理方式不同,那些語言裡 function parameters(函數參數)總是按照一個特定的順序被計算。)"new Widget" 運算式一定在 tr1::shared_ptr constructor(建構函式)能被調用之前執行,因為這個運算式的結果要作為一個 argument(實參)傳遞給 tr1::shared_ptr constructor(建構函式),但是對 priority 的調用可以在第一個,第二個或第三個執行。如果編譯器選擇第二個執行它(大概這樣能使它們產生更有效率的代碼),我們最終得到這樣一個操作順序:
- 執行 "new Widget"。
- 調用 priority。
- 調用 tr1::shared_ptr constructor(建構函式)。
但是請考慮,如果對 priority 的調用引發一個 exception(異常),將發生什麼。在這種情況下,從 "new Widget" 返回的 pointer(指標)被丟失,因為它沒有被存入我們期望能阻止 resource leaks(資源泄漏)的 tr1::shared_ptr。由於一個 exception(異常)可能會插入到一個資源被建立的時間和那個資源被交給一個 resource-managing object(資源管理對象)的時間之間,所以在對 processWidget 的調用中可能會發生一次泄漏。
避免類似問題的方法很簡單:用一個單獨的語句建立 Widget 並將它存入一個 smart pointer(智能指標),然後將這個 smart pointer(智能指標)傳遞給 processWidget:
std::tr1::shared_ptr<Widget> pw(new Widget); // store newed object
// in a smart pointer in a
// standalone statement
processWidget(pw, priority()); // this call won't leak
這樣能工作是因為編譯器在不同的語句之間重新安排操作順序的活動餘地比在一個語句之內要小得多。在這個經過修改的代碼中,"new Widget" 運算式和對 tr1::shared_ptr constructor(建構函式)的調用,與對 priority 的調用在不同的語句中,所以編譯器不會允許對 priority 的調用被插入到它們中間。
Things to Remember
- 在 standalone statements(獨立語句)中將 new 出來的對象存入 smart pointers(智能指標)。如果疏忽了這一點,當 exceptions(異常)被拋出時,可能引起微妙的 resource leaks(資源泄漏)。