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

字號(hào):

前兩篇文章討論了對(duì)象在構(gòu)造過程中(構(gòu)造函數(shù))和運(yùn)行過程中(成員函數(shù))出現(xiàn)異常時(shí)的處理情況,本文將討論最后一種情況,當(dāng)異常發(fā)生在對(duì)象的析構(gòu)銷毀過程中時(shí),又會(huì)有什么不同呢?主人公阿愚在此可以非常有把握地告訴大家,這將會(huì)有大大的不同,而且處理不善還將會(huì)毫不留情地影響到軟件系統(tǒng)的可靠性和穩(wěn)定性,后果非常嚴(yán)重。不危言聳聽了,看正文吧!
    析構(gòu)函數(shù)在什么時(shí)候被調(diào)用執(zhí)行?
    對(duì)于C++程序員來說,這個(gè)問題比較簡(jiǎn)單,但是比較愛嘮叨的阿愚還是建議應(yīng)該在此再提一提,也算回顧一下C++的知識(shí),而且這將對(duì)后面的討論和理解由一定幫助。先看一個(gè)簡(jiǎn)單的示例吧!如下:
    class MyTest_Base
    {
    public:
    virtual ~ MyTest_Base ()
    {
    cout << "銷毀一個(gè)MyTest_Base類型的對(duì)象"<< endl;
    }
    };
    void main()
    {
    try
    {
    // 構(gòu)造一個(gè)對(duì)象,當(dāng)obj對(duì)象離開這個(gè)作用域時(shí)析構(gòu)將會(huì)被執(zhí)行
    MyTest_Base obj;
    }
    catch(...)
    {
    cout << "unknow exception"<< endl;
    }
    }
    編譯運(yùn)行上面的程序,從程序的運(yùn)行結(jié)果將會(huì)表明對(duì)象的析構(gòu)函數(shù)被執(zhí)行了,但什么時(shí)候被執(zhí)行的呢?按C++標(biāo)準(zhǔn)中規(guī)定,對(duì)象應(yīng)該在離開它的作用域時(shí)被調(diào)用運(yùn)行。實(shí)際上各個(gè)廠商的C++編譯器也都滿足這個(gè)要求,拿VC來做個(gè)測(cè)試驗(yàn)證吧!,下面列出的是剛剛上面的那個(gè)小示例程序在調(diào)試時(shí)拷貝出的相關(guān)程序片段。注意其中obj對(duì)象將會(huì)在離開try block時(shí)被編譯器插入一段代碼,隱式地來調(diào)用對(duì)象的析構(gòu)函數(shù)。如下:
    325: try
    326: {
    00401311 mov dword ptr [ebp-4],0
    327: // 構(gòu)造一個(gè)對(duì)象,當(dāng)obj對(duì)象離開這個(gè)作用域時(shí)析構(gòu)將會(huì)被執(zhí)行
    328: MyTest_Base obj;
    00401318 lea ecx,[obj]
    0040131B call @ILT+40(MyTest_Base::MyTest_Base) (0040102d)
    329:
    330: } // 瞧下面,編譯器插入一段代碼,隱式地來調(diào)用對(duì)象的析構(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 << "開始準(zhǔn)備銷毀一個(gè)MyTest_Base類型的對(duì)象"<< endl;
    // 注意:在析構(gòu)函數(shù)中拋出了異常
    throw std::exception("在析構(gòu)函數(shù)中故意拋出一個(gè)異常,測(cè)試!");
    }
    void Func() throw()
    {
    throw std::exception("故意拋出一個(gè)異常,測(cè)試!");
    }
    void Other() {}
    };
    void main()
    {
    try
    {
    // 構(gòu)造一個(gè)對(duì)象,當(dāng)obj對(duì)象離開這個(gè)作用域時(shí)析構(gòu)將會(huì)被執(zhí)行
    MyTest_Base obj;
    obj.Other();
    }
    catch(std::exception e)
    {
    cout << e.what() << endl;
    }
    catch(...)
    {
    cout << "unknow exception"<< endl;
    }
    }
     程序運(yùn)行的結(jié)果是:
    開始準(zhǔn)備銷毀一個(gè)MyTest_Base類型的對(duì)象
    在析構(gòu)函數(shù)中故意拋出一個(gè)異常,測(cè)試!
    從上面的程序運(yùn)行結(jié)果來看,并沒有什么特別的,在程序中首先是構(gòu)造一個(gè)對(duì)象,當(dāng)這個(gè)對(duì)象在離開它的作用域時(shí),析構(gòu)函數(shù)被調(diào)用,此時(shí)析構(gòu)函數(shù)中拋出一個(gè) std::exception類型的異常,因此后面的catch(std::exception e)塊捕獲住這個(gè)異常,并打印出異常錯(cuò)誤信息。這個(gè)過程好像顯現(xiàn)出,發(fā)生在析構(gòu)函數(shù)中的異常與其它地方發(fā)生的異常(如對(duì)象的成員函數(shù)中)并沒有什么太大的不同,除了析構(gòu)函數(shù)是隱式調(diào)用的以外,但這也絲毫不會(huì)影響到異常處理的機(jī)制呀!那究竟區(qū)別何在?玄機(jī)何在呢?繼續(xù)往下看吧!
    2、在上面的程序基礎(chǔ)上做點(diǎn)小的改動(dòng),程序代碼如下:
    void main()