溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++ 中的異常處理(五十三)

發(fā)布時(shí)間:2020-08-10 21:26:59 來(lái)源:網(wǎng)絡(luò) 閱讀:1338 作者:上帝之子521 欄目:編程語(yǔ)言

        我們?cè)谏瞎?jié)博客中講了 C 語(yǔ)言中的異常處理,今天我們就來(lái)講下 C++ 中的異常處理。在 C++ 中內(nèi)置異常處理的語(yǔ)法元素  try ... catch ...try 語(yǔ)句處理正常代碼邏輯,catch 語(yǔ)句處理異常情況,try 語(yǔ)句中的異常由相對(duì)應(yīng)的 catch 語(yǔ)句處理。C++ 通過(guò) throw 語(yǔ)句拋出異常信息,throw 拋出的異常必須被 catch 處理,當(dāng)前函數(shù)能夠處理異常,程序繼續(xù)往下執(zhí)行;當(dāng)前函數(shù)無(wú)法處理異常,則函數(shù)停止執(zhí)行并返回。未被處理的異常會(huì)順著函數(shù)調(diào)用棧向上傳播,直到被處理為止,否則程序?qū)⑼V箞?zhí)行。如下

C++ 中的異常處理(五十三)

        下來(lái)我們就以代碼為例來(lái)進(jìn)行分析

#include <iostream>
#include <string>

using namespace std;

double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    
    if( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
    }
    else
    {
        throw 0;
    }
    
    return ret;
}

int main()
{
    try
    {
        double r = divide(1, 0);
        cout << "r = " << r << endl;
    }
    catch(...)
    {
        cout << "Divided by zero..." << endl;
    }

    return 0;
}

        我們來(lái)看看編譯結(jié)果

C++ 中的異常處理(五十三)

        我們?cè)賮?lái)試試 1/1 呢

C++ 中的異常處理(五十三)

        已經(jīng)正確實(shí)現(xiàn)了哈。C++ 的這個(gè)異常處理是不是很方便呢。同一個(gè) try 語(yǔ)句是可以跟上多個(gè) catch 語(yǔ)句的。catch 語(yǔ)句可以定義具體處理的異常類型,不同類型的異常由不同的 catch 語(yǔ)句負(fù)責(zé)處理;try 語(yǔ)句中可以拋出任何類型的異常,catch(...) 用于處理所有類型的異常,任何異常都只能被捕獲(catch)一次。下來(lái)我們來(lái)看看異常處理的匹配規(guī)則,如下

C++ 中的異常處理(五十三)

         下來(lái)我們還是以代碼為例來(lái)進(jìn)行說(shuō)明

#include <iostream>
#include <string>

using namespace std;

void Demo1()
{
    try
    {
        throw 0;
    }
    catch(char c)
    {
        cout << "catch(char c)" << endl;
    }
    catch(short c)
    {
        cout << "catch(short c)" << endl;
    }
    catch(double c)
    {
        cout << "catch(double c)" << endl;
    }
    catch(int c)
    {
        cout << "catch(int c)" << endl;
    }
}

void Demo2()
{
    throw "D.T.Software";
}

int main()
{
    try
    {
        Demo1();
        //Demo2();
    }
    catch(char* s)
    {
        cout << "catch(char* s)" << endl;
    }
    catch(const char* cs)
    {
        cout << "catch(const char* cs)" << endl;
    }
    catch(string ss)
    {
        cout << "catch(string ss)" << endl;
    }
    
    return 0;
}

        我們來(lái)看看會(huì)打印出什么

C++ 中的異常處理(五十三)

        我們看到直接在最后匹配到了 int,因?yàn)閽伋龅?0 默認(rèn)類型為 int,它不會(huì)進(jìn)行默認(rèn)類型的轉(zhuǎn)換。我們?cè)賮?lái)看看 Demo2 會(huì)打印出什么

C++ 中的異常處理(五十三)

        因?yàn)樽址亲置媪?,所以它?huì)匹配到 const char* cs 上,如果我們?cè)?Demo2 函數(shù)中拋出的是 string("D.T.Software");看看會(huì)打印出什么

C++ 中的異常處理(五十三)

        便會(huì)打印出字符串了。那么在 catch語(yǔ)句中我們還可以拋出異常,如下

C++ 中的異常處理(五十三)

        那么我們?yōu)槭裁匆?catch 語(yǔ)句中重新拋出異常呢?catch 中捕獲的異??梢员恢匦陆忉尯髵伋?,在工程開(kāi)發(fā)中使用這樣的方式統(tǒng)一異常類型,如下

C++ 中的異常處理(五十三)

        那么我們還是以代碼為例來(lái)進(jìn)行講解

#include <iostream>
#include <string>

using namespace std;

void Demo()
{
    try
    {
        try
        {
            throw 'c';
        }
        catch(int i)
        {
            cout << "Inner: catch(int i)" << endl;
            throw i;
        }
        catch(...)
        {
            cout << "Inner: catch(...)" << endl;
            throw;
        }
    }
    catch(...)
    {
        cout << "Outer: catch(...)" << endl;
    }
}

/*
    假設(shè): 當(dāng)前的函數(shù)式第三方庫(kù)中的函數(shù),因此,我們無(wú)法修改源代碼
    
    函數(shù)名: void func(int i)
    拋出異常的類型: int
                        -1 ==> 參數(shù)異常
                        -2 ==> 運(yùn)行異常
                        -3 ==> 超時(shí)異常
*/

void func(int i)
{
    if( i < 0 )
    {
        throw -1;
    }
    
    if( i > 100 )
    {
        throw -2;
    }
    
    if( i == 11 )
    {
        throw -3;
    }
    
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw "Invalid Parameter";
                break;
            case -2:
                throw "Runtime Exception";
                break;
            case -3:
                throw "Timeout Exception";
                break;
        }
    }
}

int main()
{
    Demo();
/*    
    try
    {
        MyFunc(11);
    }
    catch(const char* cs)
    {
        cout << "Exception Info: " << cs << endl;
    }
*/    
    return 0;
}

        我們先以 Demo 函數(shù)為例來(lái)進(jìn)行分析,在 try 語(yǔ)句中的 try 語(yǔ)句里拋出 c,匹配到 catch(...) 語(yǔ)句中,先打印出 Inner: catch(...),再次拋出。匹配到外面的 catch(...) 語(yǔ)句中,先打印出 Outer: catch(...)。我們來(lái)看看結(jié)果

C++ 中的異常處理(五十三)

        我們看到和我們的分析是完全一致的。接下來(lái)的 func 函數(shù)就比如是第三方的源碼,我們得根據(jù)這個(gè)功能寫個(gè)一個(gè)屬于我們自己的 func 函數(shù)。我們改寫完之后是不是就一目了然呢?比如沒(méi)改寫之前,拋出個(gè) 11 的異常,對(duì)應(yīng)的便會(huì)打印出 -3,我們還得去查這個(gè) -3 代表啥意思。我們來(lái)注釋掉 Demo 函數(shù),看看下面的編譯結(jié)果

C++ 中的異常處理(五十三)

        輸出結(jié)果一目了然,直接看到是超時(shí)異常。那么我們便直接定位到了問(wèn)題,這樣效率便會(huì)提高。在 C++ 中,異常的類型可以是自定義類類型,對(duì)于類類型異常的匹配依舊是至上而下嚴(yán)格匹配,賦值兼容性原則在異常匹配中依然適用。一般而言,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。在工程中會(huì)定義一系列的異常類,每個(gè)類代表工程中可能出現(xiàn)的一種異常類型。代碼復(fù)用時(shí)可能需要解釋不同的異常類,在定義 catch 語(yǔ)句時(shí)需要推薦使用引用作為參數(shù)。

        接下來(lái)我們還是以代碼為例來(lái)進(jìn)行分析

#include <iostream>
#include <string>

using namespace std;

class Base
{
};

class Exception : public Base
{
    int m_id;
    string m_desc;
public:
    Exception(int id, string desc)
    {
        m_id = id;
        m_desc = desc;
    }
    
    int id() const
    {
        return m_id;
    }
    
    string description() const
    {
        return m_desc;
    }
};

/*
    假設(shè): 當(dāng)前的函數(shù)式第三方庫(kù)中的函數(shù),因此,我們無(wú)法修改源代碼
    
    函數(shù)名: void func(int i)
    拋出異常的類型: int
                        -1 ==> 參數(shù)異常
                        -2 ==> 運(yùn)行異常
                        -3 ==> 超時(shí)異常
*/

void func(int i)
{
    if( i < 0 )
    {
        throw -1;
    }
    
    if( i > 100 )
    {
        throw -2;
    }
    
    if( i == 11 )
    {
        throw -3;
    }
    
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw Exception(-1, "Invalid Parameter");
                break;
            case -2:
                throw Exception(-2, "Runtime Exception");
                break;
            case -3:
                throw Exception(-3, "Timeout Exception");
                break;
        }
    }
}

int main()
{
    try
    {
        MyFunc(11);
    }
    catch(const Exception& e)
    {
        cout << "Exception Info: " << endl;
        cout << "    ID: " << e.id() << endl;
        cout << "    Description: " << e.description() << endl;
    }
    catch(const Base& e)
    {
        cout << "catch(const Base& e)" << endl;
    }

    return 0;
}

        我們看到定義了兩個(gè)類,在類 Exception 中定義了 id 和 description 用來(lái)描述他們的信息,再在 MyFunc 函數(shù)中生成臨時(shí)對(duì)象 Exception 用來(lái)獲取他們的信息,我們來(lái)看看編譯結(jié)果

C++ 中的異常處理(五十三)

        這樣的信息是不是更加直觀呢。如果我們將上面的 catch 語(yǔ)句中的父類放在子類前面呢,看看結(jié)果

C++ 中的異常處理(五十三)

        我們看到編譯已經(jīng)警告了,運(yùn)行后它打印的是父類的信息,因?yàn)樗瑯幼裱x值兼容性原則。我們?cè)谥罢f(shuō)的,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。一定要遵循這個(gè)規(guī)則。在 C++ 標(biāo)準(zhǔn)庫(kù)中提供了實(shí)用異常類族,標(biāo)準(zhǔn)庫(kù)中的異常都是從 exception 類派生的,exception 類有兩個(gè)主要的分支:a> logic_error 常用于程序中可避免邏輯錯(cuò)誤;b> runtime_error 常用于程序中無(wú)法避免的惡性錯(cuò)誤。下圖是標(biāo)準(zhǔn)庫(kù)中的異常類關(guān)系

C++ 中的異常處理(五十三)

        通過(guò)對(duì)異常的學(xué)習(xí),總結(jié)如下:1、C++ 中直接支持異常處理的概念;2、try...catch..是 C++ 中異常處理的專用語(yǔ)句;3、try 語(yǔ)句處理正常代碼邏輯,catch 語(yǔ)句處理異常情況,同一個(gè) try 語(yǔ)句可以跟上多個(gè) catch 語(yǔ)句;4、異常處理必須嚴(yán)格匹配,不進(jìn)行任何的轉(zhuǎn)換;5、catch 語(yǔ)句塊中可以拋出異常,異常的類型可以是自定義類類型;6、賦值兼容性原則在異常匹配中依然適用;7、標(biāo)準(zhǔn)庫(kù)中的異常都是從 exception 類派生的。


        歡迎大家一起來(lái)學(xué)習(xí) C++ 語(yǔ)言,可以加我QQ:243343083。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI