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

        咱們在上節博客中講了 C 語言中的異常處理,今天咱們就來說下 C++ 中的異常處理。在 C++ 中內置異常處理的語法元素  try ... catch ...try 語句處理正常代碼邏輯,catch 語句處理異常狀況,try 語句中的異常由相對應的 catch 語句處理。C++ 經過 throw 語句拋出異常信息,throw 拋出的異常必須被 catch 處理,當前函數可以處理異常,程序繼續往下執行;當前函數沒法處理異常,則函數中止執行並返回。未被處理的異常會順着函數調用棧向上傳播,直到被處理爲止,不然程序將中止執行。以下ios

圖片.png

        下來咱們就以代碼爲例來進行分析ide

#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;
}

        咱們來看看編譯結果函數

圖片.png

        咱們再來試試 1/1 呢學習

圖片.png

        已經正確實現了哈。C++ 的這個異常處理是否是很方便呢。同一個 try 語句是能夠跟上多個 catch 語句的。catch 語句能夠定義具體處理的異常類型,不一樣類型的異常由不一樣的 catch 語句負責處理;try 語句中能夠拋出任何類型的異常,catch(...) 用於處理全部類型的異常,任何異常都只能被捕獲(catch)一次。下來咱們來看看異常處理的匹配規則,以下spa

圖片.png

         下來咱們仍是以代碼爲例來進行說明對象

#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;
}

        咱們來看看會打印出什麼圖片

圖片.png

        咱們看到直接在最後匹配到了 int,由於拋出的 0 默認類型爲 int,它不會進行默認類型的轉換。咱們再來看看 Demo2 會打印出什麼ip

圖片.png

        由於字符串是字面量,因此它會匹配到 const char* cs 上,若是咱們在 Demo2 函數中拋出的是 string("D.T.Software");看看會打印出什麼開發

圖片.png

        便會打印出字符串了。那麼在 catch語句中咱們還能夠拋出異常,以下字符串

圖片.png

        那麼咱們爲何要在 catch 語句中從新拋出異常呢?catch 中捕獲的異常能夠被從新解釋後拋出,在工程開發中使用這樣的方式統一異常類型,以下

圖片.png

        那麼咱們仍是以代碼爲例來進行講解

#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;
    }
}

/*
    假設: 當前的函數式第三方庫中的函數,所以,咱們沒法修改源代碼
    
    函數名: void func(int i)
    拋出異常的類型: int
                        -1 ==> 參數異常
                        -2 ==> 運行異常
                        -3 ==> 超時異常
*/

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 函數爲例來進行分析,在 try 語句中的 try 語句裏拋出 c,匹配到 catch(...) 語句中,先打印出 Inner: catch(...),再次拋出。匹配到外面的 catch(...) 語句中,先打印出 Outer: catch(...)。咱們來看看結果

圖片.png

        咱們看到和咱們的分析是徹底一致的。接下來的 func 函數就好比是第三方的源碼,咱們得根據這個功能寫個一個屬於咱們本身的 func 函數。咱們改寫完以後是否是就一目瞭然呢?好比沒改寫以前,拋出個 11 的異常,對應的便會打印出 -3,咱們還得去查這個 -3 表明啥意思。咱們來註釋掉 Demo 函數,看看下面的編譯結果

圖片.png

        輸出結果一目瞭然,直接看到是超時異常。那麼咱們便直接定位到了問題,這樣效率便會提升。在 C++ 中,異常的類型能夠是自定義類類型,對於類類型異常的匹配依舊是至上而下嚴格匹配,賦值兼容性原則在異常匹配中依然適用。通常而言,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。在工程中會定義一系列的異常類,每一個類表明工程中可能出現的一種異常類型。代碼複用時可能須要解釋不一樣的異常類,在定義 catch 語句時須要推薦使用引用做爲參數。

        接下來咱們仍是以代碼爲例來進行分析

#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;
    }
};

/*
    假設: 當前的函數式第三方庫中的函數,所以,咱們沒法修改源代碼
    
    函數名: void func(int i)
    拋出異常的類型: int
                        -1 ==> 參數異常
                        -2 ==> 運行異常
                        -3 ==> 超時異常
*/

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;
}

        咱們看到定義了兩個類,在類 Exception 中定義了 id 和 description 用來描述他們的信息,再在 MyFunc 函數中生成臨時對象 Exception 用來獲取他們的信息,咱們來看看編譯結果

圖片.png

        這樣的信息是否是更加直觀呢。若是咱們將上面的 catch 語句中的父類放在子類前面呢,看看結果

圖片.png

        咱們看到編譯已經警告了,運行後它打印的是父類的信息,由於它一樣遵循賦值兼容性原則。咱們在以前說的,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。必定要遵循這個規則。在 C++ 標準庫中提供了實用異常類族,標準庫中的異常都是從 exception 類派生的,exception 類有兩個主要的分支:a> logic_error 經常使用於程序中可避免邏輯錯誤;b> runtime_error 經常使用於程序中沒法避免的惡性錯誤。下圖是標準庫中的異常類關係

圖片.png

        經過對異常的學習,總結以下:一、C++ 中直接支持異常處理的概念;二、try...catch..是 C++ 中異常處理的專用語句;三、try 語句處理正常代碼邏輯,catch 語句處理異常狀況,同一個 try 語句能夠跟上多個 catch 語句;四、異常處理必須嚴格匹配,不進行任何的轉換;五、catch 語句塊中能夠拋出異常,異常的類型能夠是自定義類類型;六、賦值兼容性原則在異常匹配中依然適用;七、標準庫中的異常都是從 exception 類派生的。


        歡迎你們一塊兒來學習 C++ 語言,能夠加我QQ:243343083

相關文章
相關標籤/搜索