上篇文章提到了 C++ 類建構函式對成員變數初始化賦值時使用“初始化列表”的方式帶來的好處。這篇文章將介紹另一個大大的好處——由初始化列表異常塊機制帶來的建構函式的安全性機制。
聽起來有些拗口。直接上代碼:
class Fck<br />{<br /> int* sbArray;<br />public:<br /> Fck ( int sbNum );<br /> ~Fck ();<br />};<br />Fck :: Fck ( int sbNum )<br /> try: sbArray ( new int[sbNum] )<br />{<br /> cout << " Fck Constructing " << sbNum << endl;<br />}<br />catch ( bad_alloc& err )<br />{<br /> cout << err.what() << endl;<br /> cout << " Fck Failed " << endl;<br />};<br />Fck :: ~Fck ()<br />{<br /> delete this->sbArray;<br /> cout << " Fck Destructing " << endl;<br />}<br />int _tmain(int argc, _TCHAR* argv[])<br />{<br /> Fck fck ( 5 );<br /> //Fck fck_1 ( -1 );<br /> try<br /> {<br /> Fck fck_1 ( -1 );<br /> }<br /> catch ( bad_alloc& err )<br /> {<br /> cout << err.what() << endl;<br /> cout << " Main Failed " << endl;<br /> }<br /> catch ( ... ) { }<br /> system ( "pause" );<br /> return 0;<br />}
《C++ 異常機制 32 點》這篇文章裡第 20 點談到了這種用法。Fck 類的建構函式中需要對類成員變數 int* 型的 sbArray 指定一塊新開闢出來的堆記憶體空間。這裡有一個問題,若傳遞進去的 sbNum 是負值當如何?當然會報異常。注意到建構函式後緊跟著一個 catch 塊用於捕獲初始化列表 try 塊產生的異常。完了嗎?沒有,儘管原代碼沒有寫,但是,在執行完這段 catch 塊之後,會緊跟著再將此異常“外拋”出去!這個通過查看彙編碼就知道了,在該 catch 塊完成後,系統會自動再調一次 throw,在此就不展開細說了,讀者可自行驗證。這有點像 JAVA 裡面的“上拋機制”。拋給誰呢?當然是給主函數了,於是,主函數必須還得有一個 try – catch 塊去處理異常。
這麼麻煩?這樣做是有很大的好處的。試想,若成員構造時發生了異常,此時這個類對像是構造失敗的,一個構造失敗的類對像是不可以使用的。建構函式必須通知主函數自己是否正常。這一機制保證了這一過程的安全。
你當然也可以這麼來寫建構函式,即不使用初始化列表的方式:
Fck :: Fck ( int sbNum )<br />{<br /> try<br /> {<br /> sbArray = new int[sbNum];<br /> }<br /> catch ( ... ) {}<br /> cout << " Fck Constructing " << sbNum << endl;<br />}
這樣,異常在建構函式內處理完了,不會再“上拋”,主函數根本就不知道這個類對像是構造失敗的,使用中必然會出現不可預知的錯誤!
若有讀者對此仍存疑,或者想瞭解更多,請參閱《續:為何說 C++ 建構函式初始化列表異常機制是必要的》。
參考:
《C++ 0x(C++ 09)新標準全部革新提案文檔列表》