前兩篇文章討論了對象在構(gòu)造過程中(構(gòu)造函數(shù))和運行過程中(成員函數(shù))出現(xiàn)異常時的處理情況,本文將討論最后一種情況,當異常發(fā)生在對象的析構(gòu)銷毀過程中時,又會有什么不同呢?主人公阿愚在此可以非常有把握地告訴大家,這將會有大大的不同,而且處理不善還將會毫不留情地影響到軟件系統(tǒng)的可靠性和穩(wěn)定性,后果非常嚴重。不危言聳聽了,看正文吧!
析構(gòu)函數(shù)在什么時候被調(diào)用執(zhí)行?
對于C++程序員來說,這個問題比較簡單,但是比較愛嘮叨的阿愚還是建議應該在此再提一提,也算回顧一下C++的知識,而且這將對后面的討論和理解由一定幫助。先看一個簡單的示例吧!如下:
class MyTest_Base
{
public:
virtual ~ MyTest_Base ()
{
cout << "銷毀一個MyTest_Base類型的對象"<< endl;
}
};
void main()
{
try
{
// 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
MyTest_Base obj;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}
編譯運行上面的程序,從程序的運行結(jié)果將會表明對象的析構(gòu)函數(shù)被執(zhí)行了,但什么時候被執(zhí)行的呢?按C++標準中規(guī)定,對象應該在離開它的作用域時被調(diào)用運行。實際上各個廠商的C++編譯器也都滿足這個要求,拿VC來做個測試驗證吧!,下面列出的是剛剛上面的那個小示例程序在調(diào)試時拷貝出的相關程序片段。注意其中obj對象將會在離開try block時被編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)。如下:
325: try
326: {
00401311 mov dword ptr [ebp-4],0
327: // 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
328: MyTest_Base obj;
00401318 lea ecx,[obj]
0040131B call @ILT+40(MyTest_Base::MyTest_Base) (0040102d)
329:
330: } // 瞧下面,編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)
00401320 lea ecx,[obj]
00401323 call @ILT+15(MyTest_Base::~MyTest_Base) (00401014)
331: catch(...)
00401328 jmp __tryend$_main$1 (00401365)
332: {
333: cout << "unknow exception"<< endl;
0040132A mov esi,esp
0040132C mov eax,[__imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z (0041610c)
00401331 push eax
00401332 mov edi,esp
00401334 push offset string "unknow exception" (0041401c)
00401339 mov ecx,dword ptr [__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (00416124)
0040133F push ecx
00401340 call dword ptr [__imp_??6std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z (004
00401346 add esp,8
00401349 cmp edi,esp
0040134B call _chkesp (004016b2)
00401350 mov ecx,eax
00401352 call dword ptr [__imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01
00401358 cmp esi,esp
0040135A call _chkesp (004016b2)
334: }
0040135F mov eax,offset __tryend$_main$1 (00401365)
00401364 ret
335: }
析構(gòu)函數(shù)中拋出的異常
1、仍然是先看示例,如下:
class MyTest_Base
{
public:
virtual ~ MyTest_Base ()
{
cout << "開始準備銷毀一個MyTest_Base類型的對象"<< endl;
// 注意:在析構(gòu)函數(shù)中拋出了異常
throw std::exception("在析構(gòu)函數(shù)中故意拋出一個異常,測試!");
}
void Func() throw()
{
throw std::exception("故意拋出一個異常,測試!");
}
void Other() {}
};
void main()
{
try
{
// 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
MyTest_Base obj;
obj.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}
程序運行的結(jié)果是:
開始準備銷毀一個MyTest_Base類型的對象
在析構(gòu)函數(shù)中故意拋出一個異常,測試!
從上面的程序運行結(jié)果來看,并沒有什么特別的,在程序中首先是構(gòu)造一個對象,當這個對象在離開它的作用域時,析構(gòu)函數(shù)被調(diào)用,此時析構(gòu)函數(shù)中拋出一個 std::exception類型的異常,因此后面的catch(std::exception e)塊捕獲住這個異常,并打印出異常錯誤信息。這個過程好像顯現(xiàn)出,發(fā)生在析構(gòu)函數(shù)中的異常與其它地方發(fā)生的異常(如對象的成員函數(shù)中)并沒有什么太大的不同,除了析構(gòu)函數(shù)是隱式調(diào)用的以外,但這也絲毫不會影響到異常處理的機制呀!那究竟區(qū)別何在?玄機何在呢?繼續(xù)往下看吧!
2、在上面的程序基礎上做點小的改動,程序代碼如下:
void main()
析構(gòu)函數(shù)在什么時候被調(diào)用執(zhí)行?
對于C++程序員來說,這個問題比較簡單,但是比較愛嘮叨的阿愚還是建議應該在此再提一提,也算回顧一下C++的知識,而且這將對后面的討論和理解由一定幫助。先看一個簡單的示例吧!如下:
class MyTest_Base
{
public:
virtual ~ MyTest_Base ()
{
cout << "銷毀一個MyTest_Base類型的對象"<< endl;
}
};
void main()
{
try
{
// 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
MyTest_Base obj;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}
編譯運行上面的程序,從程序的運行結(jié)果將會表明對象的析構(gòu)函數(shù)被執(zhí)行了,但什么時候被執(zhí)行的呢?按C++標準中規(guī)定,對象應該在離開它的作用域時被調(diào)用運行。實際上各個廠商的C++編譯器也都滿足這個要求,拿VC來做個測試驗證吧!,下面列出的是剛剛上面的那個小示例程序在調(diào)試時拷貝出的相關程序片段。注意其中obj對象將會在離開try block時被編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)。如下:
325: try
326: {
00401311 mov dword ptr [ebp-4],0
327: // 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
328: MyTest_Base obj;
00401318 lea ecx,[obj]
0040131B call @ILT+40(MyTest_Base::MyTest_Base) (0040102d)
329:
330: } // 瞧下面,編譯器插入一段代碼,隱式地來調(diào)用對象的析構(gòu)函數(shù)
00401320 lea ecx,[obj]
00401323 call @ILT+15(MyTest_Base::~MyTest_Base) (00401014)
331: catch(...)
00401328 jmp __tryend$_main$1 (00401365)
332: {
333: cout << "unknow exception"<< endl;
0040132A mov esi,esp
0040132C mov eax,[__imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z (0041610c)
00401331 push eax
00401332 mov edi,esp
00401334 push offset string "unknow exception" (0041401c)
00401339 mov ecx,dword ptr [__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (00416124)
0040133F push ecx
00401340 call dword ptr [__imp_??6std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z (004
00401346 add esp,8
00401349 cmp edi,esp
0040134B call _chkesp (004016b2)
00401350 mov ecx,eax
00401352 call dword ptr [__imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01
00401358 cmp esi,esp
0040135A call _chkesp (004016b2)
334: }
0040135F mov eax,offset __tryend$_main$1 (00401365)
00401364 ret
335: }
析構(gòu)函數(shù)中拋出的異常
1、仍然是先看示例,如下:
class MyTest_Base
{
public:
virtual ~ MyTest_Base ()
{
cout << "開始準備銷毀一個MyTest_Base類型的對象"<< endl;
// 注意:在析構(gòu)函數(shù)中拋出了異常
throw std::exception("在析構(gòu)函數(shù)中故意拋出一個異常,測試!");
}
void Func() throw()
{
throw std::exception("故意拋出一個異常,測試!");
}
void Other() {}
};
void main()
{
try
{
// 構(gòu)造一個對象,當obj對象離開這個作用域時析構(gòu)將會被執(zhí)行
MyTest_Base obj;
obj.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}
程序運行的結(jié)果是:
開始準備銷毀一個MyTest_Base類型的對象
在析構(gòu)函數(shù)中故意拋出一個異常,測試!
從上面的程序運行結(jié)果來看,并沒有什么特別的,在程序中首先是構(gòu)造一個對象,當這個對象在離開它的作用域時,析構(gòu)函數(shù)被調(diào)用,此時析構(gòu)函數(shù)中拋出一個 std::exception類型的異常,因此后面的catch(std::exception e)塊捕獲住這個異常,并打印出異常錯誤信息。這個過程好像顯現(xiàn)出,發(fā)生在析構(gòu)函數(shù)中的異常與其它地方發(fā)生的異常(如對象的成員函數(shù)中)并沒有什么太大的不同,除了析構(gòu)函數(shù)是隱式調(diào)用的以外,但這也絲毫不會影響到異常處理的機制呀!那究竟區(qū)別何在?玄機何在呢?繼續(xù)往下看吧!
2、在上面的程序基礎上做點小的改動,程序代碼如下:
void main()