More effective c++ 條款10(下) (轉)
條款10:在構造中防止資源洩漏(下)
你可能已經注意到BookEntry建構函式的catch塊中的語句與在BookEntry的解構函式的語句幾乎一樣。這裡的程式碼重複是絕對不可容忍的,所以最好的方法是把通用程式碼移入一個私有helper function中,讓建構函式與解構函式都它。:namespace prefix = o ns = "urn:schemas--com::office" />
class BookEntry {
public:
... // 同上
private:
...
void cleanup(); // 通用清除程式碼
};
void BookEntry::cleanup()
{
delete theImage;
delete theAudioClip;
}
BookEntry::BookEntry(const string& name,
const string& address,
const string& imageFileName,
const string& audioClipFileName)
: theName(name), theAddress(address),
theImage(0), theAudioClip(0)
{
try {
... // 同上
}
catch (...) {
cleanup(); // 釋放資源
throw; // 傳遞異常
}
}
BookEntry::~BookEntry()
{
cleanup();
}
這就行了,但是它沒有考慮到下面這種情況。假設我們略微改動一下設計,讓theImage
和theAudioClip
是常量(constant)指標型別:
class BookEntry {
public:
... // 同上
private:
...
Image * const theImage; // 指標現在是
AudioCl* const theAudioClip; // const型別
};
必須透過BookEntry建構函式的成員初始化表來初始化這樣的指標,因為再也沒有其它地方可以給const指標賦值(參見Effective C++條款12)。通常會這樣初始化theImage和theAudioClip:
// 一個可能在異常丟擲時導致資源洩漏的實現方法
BookEntry::BookEntry(const string& name,
const string& address,
const string& imageFileName,
const string& audioClipFileName)
: theName(name), theAddress(address),
theImage(imageFileName != ""
? new Image(imageFileName)
: 0),
theAudioClip(audioClipFileName != ""
? new AudioClip(audioClipFileName)
: 0)
{}
這樣做導致我們原先一直想避免的問題重新出現:如果theAudioClip初始化時一個異常被丟擲,theImage所指的不會被釋放。而且我們不能透過在建構函式中增加try和catch 語句來解決問題,因為try和catch是語句,而成員初始化表僅允許有(這就是為什麼我們必須在 theImage
和 theAudioClip
的初始化中使用?:以代替if
-then
-else
的原因)。
無論如何,在異常傳遞之前完成清除工作的唯一的方法就是捕獲這些異常,所以如果我們不能在成員初始化表中放入try和catch語句,我們把它們移到其它地方。一種可能是在私有成員函式中,用這些函式返回指標,指向初始化過的theImage
和 theAudioClip
物件。
class BookEntry {
public:
... // 同上
private:
... // 資料成員同上
Image * initImage(const string& imageFileName);
AudioClip * initAudioClip(const string&
audioClipFileName);
};
BookEntry::BookEntry(const string& name,
const string& address,
const string& imageFileName,
const string& audioClipFileName)
: theName(name), theAddress(address),
theImage(initImage(imageFileName)),
theAudioClip(initAudioClip(audioClipFileName))
{}
// theImage 被首先初始化,所以即使這個初始化失敗也
// 不用擔心資源洩漏,這個函式不用進行異常處理。
Image * BookEntry::initImage(const string& imageFileName)
{
if (imageFileName != "") return new Image(imageFileName);
else return 0;
}
// theAudioClip被第二個初始化, 所以如果在theAudioClip
// 初始化過程中丟擲異常,它必須確保theImage的資源被釋放。
// 因此這個函式使用try...catch 。
AudioClip * BookEntry::initAudioClip(const string&
audioClipFileName)
{
try {
if (audioClipFileName != "") {
return new AudioClip(audioClipFileName);
}
else return 0;
}
catch (...) {
delete theImage;
throw;
}
}
上面的的確不錯,也解決了令我們頭疼不已的問題。不過也有缺點,在原則上應該屬於建構函式的程式碼卻分散在幾個函式里,這令我們很難維護。
更好的解決方法是採用條款9的建議,把theImage
和 theAudioClip
指向的物件做為一個資源,被一些區域性物件管理。這個解決方法建立在這樣一個事實基礎上:theImage
和theAudioClip
是兩個指標,指向動態分配的物件,因此當指標消失的時候,這些物件應該被刪除。auto_ptr類就是基於這個目的而設計的。(參見條款9)因此我們把theImage
和 theAudioClip raw
指標型別改成對應的auto_ptr型別。
class BookEntry {
public:
... // 同上
private:
...
const auto_ptr const auto_ptr }; 這樣做使得BookEntry的建構函式即使在存在異常的情況下也能做到不洩漏資源,而且讓我們能夠使用成員初始化表來初始化 BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName) : theName(name), theAddress(address), theImage(imageFileName != "" ? new Image(imageFileName) : 0), theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : 0) {} 在這裡,如果在初始化theAudioClip時丟擲異常,theImage已經是一個被完全構造的物件,所以它能被自動刪除掉,就象 BookEntry::~BookEntry() {} // nothing to do! 這表示你能完全去掉BookEntry的解構函式。 綜上所述,如果你用對應的auto_ptr物件替代指標成員變數,就可以防止建構函式在存在異常時發生資源洩漏,你也不用手工在解構函式中釋放資源,並且你還能象以前使用非const指標一樣使用const指標,給其賦值。 在物件構造中,處理各種丟擲異常的可能,是一個棘手的問題,但是auto_ptr(或者類似於auto_ptr的類)能化繁為簡。它不僅把令人不好理解的程式碼隱藏起來,而且使得程式在面對異常的情況下也能保持正常執行。theImage
和 theAudioClip
,如下所示:
theName
, theAddress
和thePhones
一樣。而且因為theImage
和 theAudioClip
現在是包含在BookEntry中的物件,當BookEntry被刪除時它們能被自動地刪除。因此不需要手工刪除它們所指向的物件。可以這樣簡化BookEntry的解構函式:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1007835/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Effective Modern C++ 系列之 條款2: autoC++
- Effective C++ 條款08_不止於此C++
- Effective c++條款11:在operator=中處理“自我賦值”C++賦值
- [讀書筆記][effective C++]條款30-inline的原理筆記C++inline
- 文獻學習——Making Deduction More Effective in SAT Solvers
- 學懂現代C++——《Effective Modern C++》之轉向現代C++C++
- effective C++ : CHAPTER 8C++APT
- Effective C++筆記C++筆記
- 【Effective Modern C++】索引C++索引
- effective C++筆記1C++筆記
- 《Effective C++》讀書筆記C++筆記
- Effective C++ 4.設計與宣告C++
- Effective C++ 筆記(3)資源管理C++筆記
- 條款01: 視C++為一個語言聯邦C++
- 《Effective C++》閱讀總結(三):資源管理C++
- Linux下more命令高階用法Linux
- 《Effective C++》第三版-1. 讓自己習慣C++(Accustoming Yourself to C++)C++
- 學懂現代C++——《Effective Modern C++》之型別推導和autoC++型別
- 條款05: 瞭解c++默默編寫並呼叫哪些函式C++函式
- 《Effective C++》第三版-5. 實現(Implementations)C++
- 讀完Java名著《Effective Java》: 我整理了這50條技巧Java
- 《Effective C++》閱讀總結(四): 設計、宣告與實現C++
- 《Effective C++》第三版-3. 資源管理(Resource Management)C++
- c++切面條題目C++
- 《Effective C++》第三版-4. 設計與宣告(Design and Declarations)C++
- 千萬別下!這 10 款 App 違法!APP
- 《Effective Python 第二版》第二條 遵循PEP8風格指南Python
- 《Effective C++》閱讀總結(二):類的構造、析構和賦值C++賦值
- 《看雪服務條款》
- 玩轉 iOS 10 推送 —— UserNotifications Framework(下)iOSFramework
- 【C++】C++之型別轉換C++型別
- C++中的條件變數C++變數
- MORE_DETAIL_TECHAI
- TypeScript 之 More on FunctionsTypeScriptFunction
- More web function requests go online concurrently, and web service deployment is faster and more economical!WebFunctionGoAST
- [20180625]10g下查詢條件rownum = 0.txt
- effective java 觀後感Java
- Vegetables need more practice.
- B-A Bit More Common