# Item 17: 在一個獨立的語句中將 new 出來的對象存入智能指針
作者:Scott Meyers
譯者:fatalerror99 (iTePub's Nirvana)
發布:http://blog.csdn.net/fatalerror99/
假設我們有一個函數取得我們的處理優先級,而第二個函數根據優先級針對動態分配的 Widget 做一些處理:
```
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
```
不要忘記使用對象管理資源的至理名言(參見 Item 13),processWidget 為處理動態分配的 Widget 使用了一個智能指針(在此,是一個 tr1::shared_ptr)。
現在考慮一個對 processWidget 的調用:
```
processWidget(new Widget, priority());
```
且慢,別想這樣調用。它不能編譯。tr1::shared_ptr 的構造函數取得一個裸指針(raw pointer)應該是顯式的,所以不能從一個由 "new Widget" 返回的裸指針隱式轉型到 processWidget 所需要的 tr1::shared_ptr。下面的代碼,無論如何,是可以編譯的:
```
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
```
令人驚訝的是,盡管我們在這里各處都使用了對象管理資源,這個調用還是可能泄漏資源。下面就來說明這是如何發生的。
在編譯器能生成一個對 processWidget 的調用之前,它們必須傳遞實際參數來計算形式參數的值。第二個實際參數不過是對函數 priority 的調用,但是第一個實際參數("std::tr1::shared_ptr<Widget>(new Widget)"),由兩部分組成
* 表達式 "new Widget" 的執行。
* 一個對 tr1::shared_ptr 的構造函數的調用。
在 processWidget 能被調用之前,編譯器必須為這三件事情生成代碼:
* 調用 priority。
* 執行 "new Widget"。
* 調用 tr1::shared_ptr 的構造函數。
C++ 編譯器允許在一個相當大的范圍內決定這三件事被完成的順序。(這里與 Java 和 C# 等語言的處理方式不同,那些語言里函數參數總是按照一個精確的順序被計算。)"new Widget" 表達式一定在 tr1::shared_ptr 的構造函數能被調用之前執行,因為這個表達式的結果要作為一個參數傳遞給 tr1::shared_ptr 的構造函數,但是 priority 的調用可以被第一個,第二個或第三個執行。如果編譯器選擇第二個執行它(大概這樣能使它們生成更有效率的代碼),我們最終得到這樣一個操作順序:
1\. 執行 "new Widget"。
2\. 調用 priority。
3\. 調用 tr1::shared_ptr 的構造函數。
但是請考慮,如果對 priority 的調用引發一個異常將發生什么。在這種情況下,從 "new Widget" 返回的指針被丟失,因為它沒有被存入我們期望能阻止資源泄漏的 tr1::shared_ptr。由于一個異常可能插入資源創建的時間和將資源交給一個資源管理對象的時間之間,所以調用 processWidget 可能會發生一次泄漏。
避免類似問題的方法很簡單:用一個單獨的語句創建 Widget 并將它存入一個智能指針,然后將這個智能指針傳遞給 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 的構造函數的調用與 priority 的調用在不同的語句中,所以編譯器不會允許 priority 的調用插入它們中間。
Things to Remember
* 在一個獨立的語句中將 new 出來的對象存入智能指針。如果疏忽了這一點,當異常發生時,可能引起微妙的資源泄漏。
- Preface(前言)
- Introduction(導言)
- Terminology(術語)
- Item 1: 將 C++ 視為 federation of languages(語言聯合體)
- Item 2: 用 consts, enums 和 inlines 取代 #defines
- Item 3: 只要可能就用 const
- Item 4: 確保 objects(對象)在使用前被初始化
- Item 5: 了解 C++ 為你偷偷地加上和調用了什么函數
- Item 6: 如果你不想使用 compiler-generated functions(編譯器生成函數),就明確拒絕
- Item 7: 在 polymorphic base classes(多態基類)中將 destructors(析構函數)聲明為 virtual(虛擬)
- Item 8: 防止因為 exceptions(異常)而離開 destructors(析構函數)
- Item 9: 絕不要在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數)
- Item 10: 讓 assignment operators(賦值運算符)返回一個 reference to *this(引向 *this 的引用)
- Item 11: 在 operator= 中處理 assignment to self(自賦值)
- Item 12: 拷貝一個對象的所有組成部分
- Item 13: 使用對象管理資源
- Item 14: 謹慎考慮資源管理類的拷貝行為
- Item 15: 在資源管理類中準備訪問裸資源(raw resources)
- Item 16: 使用相同形式的 new 和 delete
- Item 17: 在一個獨立的語句中將 new 出來的對象存入智能指針
- Item 18: 使接口易于正確使用,而難以錯誤使用
- Item 19: 視類設計為類型設計
- Item 20: 用 pass-by-reference-to-const(傳引用給 const)取代 pass-by-value(傳值)
- Item 21: 當你必須返回一個對象時不要試圖返回一個引用
- Item 22: 將數據成員聲明為 private
- Item 23: 用非成員非友元函數取代成員函數
- Item 24: 當類型轉換應該用于所有參數時,聲明為非成員函數
- Item 25: 考慮支持不拋異常的 swap
- Item 26: 只要有可能就推遲變量定義
- Item 27: 將強制轉型減到最少
- Item 28: 避免返回對象內部構件的“句柄”
- Item 29: 爭取異常安全(exception-safe)的代碼
- Item 30: 理解 inline 化的介入和排除
- Item 31: 最小化文件之間的編譯依賴
- Item 32: 確保 public inheritance 模擬 "is-a"
- Item 33: 避免覆蓋(hiding)“通過繼承得到的名字”
- Item 34: 區分 inheritance of interface(接口繼承)和 inheritance of implementation(實現繼承)
- Item 35: 考慮可選的 virtual functions(虛擬函數)的替代方法
- Item 36: 絕不要重定義一個 inherited non-virtual function(通過繼承得到的非虛擬函數)
- Item 37: 絕不要重定義一個函數的 inherited default parameter value(通過繼承得到的缺省參數值)
- Item 38: 通過 composition(復合)模擬 "has-a"(有一個)或 "is-implemented-in-terms-of"(是根據……實現的)
- Item 39: 謹慎使用 private inheritance(私有繼承)
- Item 40: 謹慎使用 multiple inheritance(多繼承)
- Item 41: 理解 implicit interfaces(隱式接口)和 compile-time polymorphism(編譯期多態)
- Item 42: 理解 typename 的兩個含義
- Item 43: 了解如何訪問 templatized base classes(模板化基類)中的名字
- Item 44: 從 templates(模板)中分離出 parameter-independent(參數無關)的代碼
- Item 45: 用 member function templates(成員函數模板) 接受 "all compatible types"(“所有兼容類型”)
- Item 46: 需要 type conversions(類型轉換)時在 templates(模板)內定義 non-member functions(非成員函數)
- Item 47: 為類型信息使用 traits classes(特征類)
- Item 48: 感受 template metaprogramming(模板元編程)
- Item 49: 了解 new-handler 的行為
- Item 50: 領會何時替換 new 和 delete 才有意義
- Item 51: 編寫 new 和 delete 時要遵守慣例
- Item 52: 如果編寫了 placement new,就要編寫 placement delete
- 附錄 A. 超越 Effective C++
- 附錄 B. 第二和第三版之間的 Item 映射