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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- More Effective C++ 條款27(下) (轉)C++
- More Effective C++ 條款28(下) (轉)C++
- More Effective C++ 條款26(下) (轉)C++
- More effective c++ 條款10(上) (轉)C++
- More Effective C++ 條款4 (轉)C++
- More Effective C++ 條款19 (轉)C++
- More Effective C++ 條款6 (轉)C++
- More effective C++ 條款14 (轉)C++
- More Effective C++ 條款15 (轉)C++
- More Effective C++ 條款2 (轉)C++
- More Effective C++ 條款3 (轉)C++
- More Effective C++ 條款11 (轉)C++
- More effective C++ 條款13 (轉)C++
- More effective C++ 條款12 (轉)C++
- More Effective C++ 條款5 (轉)C++
- More Effective C++ 條款一 (轉)C++
- More Effective C++ 條款17 (轉)C++
- More Effective C++ 條款18 (轉)C++
- More Effective C++ 條款20 (轉)C++
- More Effective C++ 條款21 (轉)C++
- More Effective C++ 條款22 (轉)C++
- More Effective C++ 條款23 (轉)C++
- More Effective C++ 條款24 (轉)C++
- More Effective C++ 條款25 (轉)C++
- More Effective C++ 條款7 (轉)C++
- More Effective C++ 條款8 (轉)C++
- More Effective C++ 條款28(中) (轉)C++
- More Effective C++ 條款28(上) (轉)C++
- Effective Modern C++ 系列之 條款2: autoC++
- Effective C++ 條款08_不止於此C++
- Effective c++條款11:在operator=中處理“自我賦值”C++賦值
- [讀書筆記][effective C++]條款30-inline的原理筆記C++inline
- Effective C++: Item 32 (轉)C++
- Effective C++: Item 21 (轉)C++
- Effective C++: Item 24 (轉)C++
- more effective entity bean(新的改進entity bean的效能的七條(EJB2.0版)) (轉)Bean
- 《More Effective C#》讀書筆記C#筆記
- Effective STL之條款2:謹防容器無關程式碼的假象 (轉)