程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++基礎知識 >> C++異常類型以及多級catch

C++異常類型以及多級catch

編輯:C++基礎知識
首先來回顧一下上節講到的 try-catch 的用法:

try{
    // 可能拋出異常的語句
}catch(exceptionType variable){
    // 處理異常的語句
}

我們還遺留下一個問題,就是 catch 關鍵字後邊的exceptionType variable,這節就來詳細分析一下。

exceptionType是異常類型,它指明了當前的 catch 可以處理什麼類型的異常;variable是一個變量,用來接收異常信息。當程序拋出異常時,會創建一份數據,這份數據包含了錯誤信息,程序員可以根據這些信息來判斷到底出了什麼問題,接下來怎麼處理。

異常既然是一份數據,那麼就應該有數據類型。C++ 規定,異常類型可以是 int、char、float、bool 等基本類型,也可以是指針、數組、字符串、結構體、類等聚合類型。C++ 語言本身以及標准庫中的函數拋出的異常,都是 exception 類或其子類的異常。也就是說,拋出異常時,會創建一個 exception 類或其子類的對象。

exceptionType variable和函數的形參非常類似,當異常發生後,會將異常數據傳遞給 variable 這個變量,這和函數傳參的過程類似。當然,只有跟 exceptionType 類型匹配的異常數據才會被傳遞給 variable,否則 catch 不會接收這份異常數據,也不會執行 catch 塊中的語句。換句話說,catch 不會處理當前的異常。

我們可以將 catch 看做一個沒有返回值的函數,當異常發生後 catch 會被調用,並且會接收實參(異常數據)。

但是 catch 和真正的函數調用又有區別:
  • 真正的函數調用,形參和實參的類型必須要匹配,或者可以自動轉換,否則在編譯階段就報錯了。
  • 而對於 catch,異常是在運行階段產生的,它可以是任何類型,沒法提前預測,所以不能在編譯階段判斷類型是否正確,只能等到程序運行後,真的拋出異常了,再將異常類型和 catch 能處理的類型進行匹配,匹配成功的話就“調用”當前的 catch,否則就忽略當前的 catch。

總起來說,catch 和真正的函數調用相比,多了一個「在運行階段將實參和形參匹配」的過程。

另外需要注意的是,如果不希望 catch 處理異常數據,也可以將 variable 省略掉,也即寫作:

try{
    // 可能拋出異常的語句
}catch(exceptionType){
    // 處理異常的語句
}

這樣只會將異常類型和 catch 所能處理的類型進行匹配,不會傳遞異常數據了。

多級 catch

前面的例子中,一個 try 對應一個 catch,這只是最簡單的形式。其實,一個 try 後面可以跟多個 catch:
try{
    //可能拋出異常的語句
}catch (exception_type_1 e){
    //處理異常的語句
}catch (exception_type_2 e){
    //處理異常的語句
}
//其他的catch
catch (exception_type_n e){
    //處理異常的語句
}
當異常發生時,程序會按照從上到下的順序,將異常類型和 catch 所能接收的類型逐個匹配。一旦找到類型匹配的 catch 就停止檢索,並將異常交給當前的 catch 處理(其他的 catch 不會被執行)。如果最終也沒有找到匹配的 catch,就只能交給系統處理,終止程序的運行。

下面的例子演示了多級 catch 的使用:
#include <iostream>
#include <string>
using namespace std;

class Base{ };
class Derived: public Base{ };

int main(){
    try{
        throw Derived();  //拋出自己的異常類型,實際上是創建一個Derived類型的匿名對象
        cout<<"This statement will not be executed."<<endl;
    }catch(int){
        cout<<"Exception type: int"<<endl;
    }catch(char *){
        cout<<"Exception type: cahr *"<<endl;
    }catch(Base){  //匹配成功(向上轉型)
        cout<<"Exception type: Base"<<endl;
    }catch(Derived){
        cout<<"Exception type: Derived"<<endl;
    }

    return 0;
}
運行結果:
Exception type: Base
在 catch 中,我們只給出了異常類型,沒有給出接收異常信息的變量。
本例中,我們定義了一個基類 Base,又從 Base 派生類出了 Derived。拋出異常時,我們創建了一個 Derived 類的匿名對象,也就是說,異常的類型是 Derived。

我們期望的是,異常被catch(Derived)捕獲,但是從輸出結果可以看出,異常提前被catch(Base)捕獲了,這說明 catch 在匹配異常類型時發生了向上轉型(Upcasting)。

catch 在匹配過程中的類型轉換

C/C++ 中存在多種多樣的類型轉換,以普通函數(非模板函數)為例,發生函數調用時,如果實參和形參的類型不是嚴格匹配,那麼會將實參的類型進行適當的轉換,以適應形參的類型,這些轉換包括:
  • 算數轉換:例如 int 轉換為 float,char 轉換為 int,double 轉換為 int 等。
  • 向上轉型:也就是派生類向基類的轉換,請猛擊《C++向上轉型(將派生類賦值給基類)》了解詳情。
  • const 轉換:也即將非 const 類型轉換為 const 類型,例如將 char * 轉換為 const char *。
  • 數組或函數指針轉換:如果函數形參不是引用類型,那麼數組名會轉換為數組指針,函數名也會轉換為函數指針。
  • 用戶自定的類型轉換。

catch 在匹配異常類型的過程中,也會進行類型轉換,但是這種轉換受到了更多的限制,僅能進行「向上轉型」、「const 轉換」和「數組或函數指針轉換」,其他的都不能應用於 catch。

向上轉型在上面的例子中已經發生了,下面的例子演示了 const 轉換以及數組和指針的轉換:
#include <iostream>
using namespace std;

int main(){
    int nums[] = {1, 2, 3};
    try{
        throw nums;
        cout<<"This statement will not be executed."<<endl;
    }catch(const int *){
        cout<<"Exception type: const int *"<<endl;
    }

    return 0;
}
運行結果:
Exception type: const int *

nums 本來的類型是int [3],但是 catch 中沒有嚴格匹配的類型,所以先轉換為int *,再轉換為const int *
數組也是一種類型,數組並不等價於指針,這點已在《數組和指針絕不等價,數組是另外一種類型》和《數組在什麼時候會轉換為指針》中進行了詳細講解。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved