第6集對象的成員函數(shù)中拋出的異常

字號:

C++異常處理模型除了支持面向過程的C風(fēng)格程序中的異常處理外(就是沒有面向?qū)ο蟮母拍?,完全是C程序,整個程序?qū)嶋H就是函數(shù)的集合,但卻用C++ 編譯器來編譯這樣的C程序,所以這樣的程序中是可以a使用C++的異常處理機制的,要不怎么說C++是兼容C語言的呢?但是需要注意的是,單純的C語言程序中是不能使用C++異常處理模型進(jìn)行編程的。是不是有點說拗口了?有點糊涂了呢?其實很簡單,那就是如果程序中使用了C++異常處理機制,也即代碼中有 try、catch和throw關(guān)鍵字,那么就必須使用C++編譯器來編譯這個程序。許多程序員朋友們在這里有一個理解上的誤區(qū),認(rèn)為只有程序中使用了面向?qū)ο蟮母拍?,即使用class關(guān)鍵字來定義一個類結(jié)構(gòu)才算得上C++程序,其實這種理解是片面的,如果程序中采用了C++異常處理機制,那么也有理由認(rèn)為這是一個C++程序,哪怕程序的代碼完全是C語言風(fēng)格的,并且這樣的程序用C編譯器來編譯肯定將會報錯,提示未定義的try標(biāo)示符等等錯誤信息),還支持面向?qū)ο蟪绦蛑袑ο髵伋龅漠惓L幚怼?BR>    C++異常處理模型的確和面向?qū)ο笫蔷o密結(jié)合的,除了在相遇篇中介紹到的用對象來描述程序中出現(xiàn)的異常之外,C++異常處理模型也對在面向?qū)ο蟪绦蛑械膶ο髮嵗鶔伋龅漠惓W髁俗钔晟频闹С趾吞幚?。也許大家會覺得這很容易,沒什么了不起的地方。但恰恰相反,實際上這才是C++異常處理模型最成功、最不可思議和最閃光的地方。而且由于C++異常處理模型對面向?qū)ο笥辛撕芎玫闹С趾图嫒荩攀沟肅++異常處理模型本身的實現(xiàn)變得特別復(fù)雜,因為它需要跟蹤每一個對象的運行情況和狀態(tài)(關(guān)于C++異常處理模型的實現(xiàn),會在愛的秘密篇中有詳細(xì)剖析)。本文和接下來的幾篇文章將講述當(dāng)對象實例拋出異常時將如何處理。
    對象的生命周期一般有三種狀態(tài):構(gòu)造、運行和析構(gòu)銷毀。因此對象拋出的異常也有這三種區(qū)別。是在對象構(gòu)造時拋出的呢?還是對象運行時拋出的呢?或是析構(gòu)對象時拋出的?這三種不同時候拋出的異常會將會產(chǎn)生不同的結(jié)果。本文首先討論最常見的一種情況,在對象運行時拋出的異常,也即執(zhí)行對象的成員函數(shù)時出現(xiàn)的異常。
    對象的成員函數(shù)拋出的異常
    1、老方法,看例子先,如下:
    class MyTest_Base
    {
    public:
    MyTest_Base (string name = “”) : m_name(name)
    {
    cout << “構(gòu)造一個MyTest_Base類型的對象,對象名為:”<    }
    virtual ~ MyTest_Base ()
    {
    cout << “銷毀一個MyTest_Base類型的對象,對象名為:”<    }
    void Func() throw()
    {
    throw std::exception(“故意拋出一個異常,測試!”);
    }
    void Other() {}
    protected:
    string m_name;
    };
    void main()
    {
    try
    {
    MyTest_Base obj1(“obj1”);
    // 調(diào)用這個成員函數(shù)將拋出一個異常,注意obj1的析構(gòu)函數(shù)會被執(zhí)行嗎?如果
    // 會,又是在什么時候被執(zhí)行呢?
    obj1.Func();
    obj1.Other();
    }
    catch(std::exception e)
    {
    cout << e.what() << endl;
    }
    catch(...)
    {
    cout << “unknow exception”<< endl;
    }
    }
    C++程序員不難看出上面的程序的運行結(jié)果,如下:
    構(gòu)造一個MyTest_Base類型的對象,對象名為:obj1
    銷毀一個MyTest_Base類型的對象,對象名為:obj1
    故意拋出一個異常,測試!
    從運行結(jié)果可以得出如下結(jié)論:
    (1) 對象的成員函數(shù)出現(xiàn)異常時,catch block能捕獲到異常,這一點就像C語言中的普通函數(shù)一樣,沒什么特別的地方;
    (2)對象的成員函數(shù)出現(xiàn)異常時,對象的析構(gòu)函數(shù)將會得到執(zhí)行(這一點很神奇吧!當(dāng)然在這里不會做過多研究,在剖析C++異常處理模型的實現(xiàn)時再做詳細(xì)的闡述),這里與C++標(biāo)準(zhǔn)中規(guī)定的面向?qū)ο蟮奶匦允窍嘁恢碌?,?gòu)造了的對象就必須保證在適當(dāng)?shù)牡胤揭鰳?gòu)它,以釋放可能的資源。因此前面說的“C++異常處理模型對面向?qū)ο筇峁┝酥С趾图嫒荨笔怯懈鶕?jù)的。而且注意它的析構(gòu)函數(shù)是在異常處理模塊之前執(zhí)行的,這一點更與C++標(biāo)準(zhǔn)中規(guī)定的面向?qū)ο蟮奶匦允且恢碌模?dāng)對象出了作用域時,它就必須要被析構(gòu)。
    2、把上面的程序小改一下,運行再看結(jié)果,如下:
    void main()
    {
    // obj1對象不在trycatch域中,注意它的析構(gòu)函數(shù)在什么時候被執(zhí)行?
    MyTest_Base obj1(“obj1”);
    try
    {
    // obj2和obj3對象都在trycatch域中,其中obj3.Func()函數(shù)被調(diào)用,因此
    // obj3會拋出異常,特別需要注意的是,obj2的析構(gòu)函數(shù)會被執(zhí)行嗎?如果
    // 會,又是在什么時候被執(zhí)行呢?
    MyTest_Base obj2(“obj2”), obj3(“obj3”);
    obj3.Other();
    // 調(diào)用這個成員函數(shù)將拋出一個異常
    obj3.Func();
    // 注意:obj4對象在構(gòu)造之前,函數(shù)中就有異常拋出。所以obj4對象將不會
    // 被構(gòu)造,當(dāng)然也不會被析構(gòu)
    MyTest_Base obj4(“obj4”);
    obj3.Other();
    }
    catch(std::exception e)
    {
    cout << e.what() << endl;
    }
    catch(...)
    {
    cout << “unknow exception”<< endl;
    }
    }
    上面的程序也難看出其運行結(jié)果,如下:
    構(gòu)造一個MyTest_Base類型的對象,對象名為:obj1