第8集析構(gòu)函數(shù)中拋出的異常

字號:

前兩篇文章討論了對象在構(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()