聲明:
本例子描述了在建構函式拋出異常會導致資源流失的過程。通過本例子,希望我們在編寫自己的建構函式的時候,要小心一些可能的異常。如果代碼對異常考慮的不夠,那麼出現異常很有可能導致我們的資源發生泄露。
例子說明一切:
主要是BookEntry類的建構函式中AudioClip成員初始化的時候出現了異常,這直接導致已經構造完成的Image對象的堆記憶體沒有被正確的釋放。仔細看一下代碼,便可知。下一節,將給出通過智能指標對這個問題有一個比較完美的解決的例子。
#include <iostream>#include <time.h>#include <memory>using namespace std;/*作者:lpstudy日期:2013-3-16內容:more effective C++異常一章第10節,在建構函式中防止資源流失,這個是資源流失的例子,可以通過列印的螢幕看到new Image的資源確實泄露了。*/#define TRACE_FUCTION_AND_LINE(fmt, ...) printf("[%30s:%4d] "fmt"\n",__FUNCTION__, __LINE__, ##__VA_ARGS__)class Image{public:Image(const string& strImgName):m_strImgName(strImgName){TRACE_FUCTION_AND_LINE();}~Image(){TRACE_FUCTION_AND_LINE();}private:const string& m_strImgName;};/*AudioClip類是在image類構造完成之後才進行構造,下面的AudioClip拋出異常可用來觀察image的解構函式實際上由於異常並沒有被調用導致image記憶體泄露。*/class AudioClip{public:AudioClip(const string& strAudioName):m_strAudioName(strAudioName){TRACE_FUCTION_AND_LINE();throw 1;//BookEntry拋出異常,導致Image析構沒有被調用}~AudioClip(){TRACE_FUCTION_AND_LINE();}private:const string& m_strAudioName;};/*BookEntry建構函式負責初始化Image和AudioClip,這樣如果Image和AudioClip的建構函式出現異常的話,BookEntry建構函式會立刻返回,這樣構造的對象就不是一個完整的對象。*/class BookEntry{public:BookEntry(const string& name, const string& address = "", const string& imageFileName = "", const string& audioclipName = ""): m_strName(name), m_strAddress(address), m_pImage(0), m_pAudioClip(0){TRACE_FUCTION_AND_LINE();if(!imageFileName.empty()){m_pImage = new Image(imageFileName);}if(!audioclipName.empty()){m_pAudioClip = new AudioClip(audioclipName);}}~BookEntry(){delete m_pImage;delete m_pAudioClip;TRACE_FUCTION_AND_LINE();}void Log () {TRACE_FUCTION_AND_LINE("My Log------"); throw 1;} private:string m_strName;string m_strAddress;Image* m_pImage;AudioClip* m_pAudioClip;};/*測試建構函式異常的時候,棧記憶體的解構函式不會被調用導致Image記憶體泄露*/void TestStackMemory(){try{TRACE_FUCTION_AND_LINE("");BookEntry bookEntry("lpstudy", "beijing", "imageFileName", "audioClipName");}catch(int e){TRACE_FUCTION_AND_LINE("exception int = %d", e);}}/*測試堆記憶體的時候,建構函式異常的時候可以看出這個時候pBookEntry還沒有完全構造出來,返回的指標實際上是NULL導致Image記憶體泄露*/void TestHeapMemory(){BookEntry *pBookEntry = 0;try{TRACE_FUCTION_AND_LINE("");pBookEntry = new BookEntry("lpstudy", "beijing", "imageFileName", "audioClipName");}catch(int e){TRACE_FUCTION_AND_LINE("exception int = %d", e);TRACE_FUCTION_AND_LINE("pBookEntry = %08p", pBookEntry);delete pBookEntry;//實際上delete null, 沒有任何意義}}int main(){TRACE_FUCTION_AND_LINE("Trace BookEntry.......");TestStackMemory();TestHeapMemory();return 0;}/*以上代碼清楚的反映了由於BookEntry建構函式異常導致已經分配的堆記憶體Image對象的解構函式不會被執行(由於BookEntry的解構函式沒有執行),導致Image記憶體泄露。解決方案:1,建構函式的函數體中加入try,catch,在catch的時候調用cleanup函數。2,方案1對如果指標是const pointer,不行。因為const pointer必須在成員初始化列表中被初始化,這個時候需要引用InitImage,InitAudioClip方法,這兩個方法中負責try,catch,並返回構造的image和audioclip對象指標,送給成員初始化列表初始化,這樣導致成員函數增多,導致代碼膨脹,維護很混亂3,方案3採用智能指標auto_ptr,不管const還是non-const指標都可以有同樣的處理結果,代碼簡單清晰,這個將代碼實現,畢竟有很大的實用價值。*/