考慮如下代碼:
void* operator new ( size_t size )<br />{<br /> printf ( "Malloc running! Size: %u/n", size );<br /> return malloc ( size );<br />}<br />void operator delete ( void* memblock )<br />{<br /> printf ( "Free running!/n" );<br /> free ( memblock );<br />}<br />class Fck<br />{<br />public:<br /> Fck () { printf ( "Fck Constructing/n" ); }<br /> ~Fck() { printf ( "Fck Destructing/n" ); }<br />};<br />int _tmain(int argc, _TCHAR* argv[])<br />{<br /> delete new Fck ();<br /> return 0;<br />}
運行之,控制台顯示資訊如下:
可以清晰地看到 new 類對像時,先分配空間再構造;delete 時,先析構再釋放空間的步驟。
再考慮如下代碼:
void* operator new ( size_t size )<br />{<br /> printf ( "Malloc running! Size: %u/n", size );<br /> return malloc ( size );<br />}<br />void operator delete ( void* memblock )<br />{<br /> printf ( "Free running!/n" );<br /> free ( memblock );<br />}<br />class Fck<br />{<br />public:<br /> Fck ()<br /> {<br /> printf ( "Fck Constructing/n" );<br /> throw int ( /*5*/ );<br /> }<br /> ~Fck() { printf ( "Fck Destructing/n" ); }<br />};<br />int _tmain(int argc, _TCHAR* argv[])<br />{<br /> try { new Fck (); }<br /> catch ( int& i ) { printf ( "Error no.: %d/n", i ); }<br /> return 0;<br />}
這裡先注釋掉 Fck 類建構函式中的那句 throw int ()。建構函式不會拋出異常。運行之,控制台顯示資訊如下:
結果很好理解。try 塊中成功 new 出 Fck 類對像,程式控制流程並未要求去顯式 delete 掉它,因此對該類對像的析構和釋放資源的過程不會執行。
問題不在這兒,若我在建構函式中拋出異常將如何?將注釋掉的那句 throw int () 恢複,則代碼將直接從建構函式中拋出異常。關於類建構函式處理異常更安全的用法請見《C++ 類建構函式初始化列表的異常機制 function-try block》和《續:為何說 C++ 建構函式初始化列表異常機制是必要的》兩篇文章。
運行之,控制台顯示資訊如下:
發現 delete 算符重載方法被執行了,資源被釋放,而沒有解構函式調用的資訊。沒有析構是可以理解的,因為對像構造失敗,對像並沒構造完成,它沒有生出來,就沒辦法去搞死它。在 new Fck () 這句設斷點,發現執行完 new 算符後,箭頭又跳了回來,然後流程再跳到建構函式中去。也就是說,new Fck () 過程的確是分兩步執行的。這句執行到第二步發生異常時,try 塊讓第一步發生了“回退”,也即執行了 new 的反操作 delete 了。這很有意思。
更多請參考:《C++異常處理須知》。