【Swift 3 && C++11】<第一部分> 概覽(7): Swift錯誤處理 與 C++ 異常處理

異常處理

|Swift|C++
:-:|:-:|:-:
關鍵字或類型|Error, throws, try, do - catch, try?, defer| throw, try...catch

C++中的異常處理機制包括:

  • throw 表達式 異常檢測部分使用throw 表達式來表示它遇到了無法處理的問題. 我們說throw 引發(fā)了異常.
  • try 語句塊, 異常處理部分使用try 語句塊處理異常. try 語句塊以關鍵字 try 開始, 并以一個或多個catch 子句結束.
  • 一套異常類, 用于在 throw 表達式和相關的 catch 子句之間傳遞異常的具體信息.

先來介紹 Swift 中的錯誤處理內(nèi)容.

Swift 中你可以使用任意的遵循了Error協(xié)議的類型來表示錯誤.

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

可以使用thow 來拋出一個錯誤并使用thows 來表示一個可以拋出錯誤的函數(shù). 如果在一個函數(shù)中拋出了一個錯誤, 這個函數(shù)會立刻返回并且調(diào)用函數(shù)的代碼會進行錯誤處理.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

有多種方式用來進行錯誤處理. 一種方式是使用do-catch.在 do代碼塊中, 使用 try來標記可以拋出錯誤的代碼. 在catch 代碼塊中,如果沒有給錯誤命名,則默認為error:

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}

練習: 將 printer name 改為 "Never Has Toner" 使 sendToPrinter(_:) 函數(shù)拋出錯誤。

可以使用多個catch 塊來處理特定的錯誤. 就像寫 switch中的多個case一樣:

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}

練習: 在 do 代碼塊中添加拋出錯誤的代碼。你需要拋出哪種錯誤來使第一個 catch 塊進行接收?怎么使第二個和第三個 catch 進行接收呢?

另一種處理錯誤的方式是使用try?將結果轉換為可選的. 如果函數(shù)拋出錯誤,該錯誤會被拋出并且結果為nil. 否則的話, 結果會是一個包含函數(shù)值的可選值:

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

可以使用defer 代碼塊來表示在函數(shù)返回前, 函數(shù)中最后執(zhí)行的代碼. 無論函數(shù)是否會拋出錯誤, 這段代碼都將執(zhí)行. 使用defer, 可以把函數(shù)調(diào)用之初就要執(zhí)行的代碼和函數(shù)調(diào)用結束時的掃尾代碼寫在一起,雖然這兩者的執(zhí)行時機截然不同:

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
print(fridgeIsOpen)

可以看出** Swift** 的錯誤處理由以下三部分組成:

  • thow來拋出錯誤
  • do-catchtry? 來進行錯誤處理.
  • Error 錯誤協(xié)議

其中thows 用來標記一個函數(shù)拋出異常的函數(shù), 而do-catchdo 代碼塊中使用try 來標記可以拋出錯誤的代碼. 而使用defer 來表示不管函數(shù)是否拋出異常都會最后被執(zhí)行的代碼.

在** C++中**也可以使用throw 來拋出異常:

#include <iostream>
using namespace std;

void doSomething(int a) {
    if (a) {
        cout << "代碼安全:" << a << endl;
    }else {
        throw "代碼出錯";
    }
}

int main() {
    
    doSomething(0);
    
    return 0;
}

如果你運行上面的例子, 你會發(fā)現(xiàn)程序直接崩潰在throw 那行, 這是因為程序發(fā)生了異常但沒有被捕獲,則它將調(diào)用標準庫函數(shù)terminate終止當前程序.

catch子句可以用來捕獲異常,即 try語句塊:

#include <iostream>
using namespace std;

void doSomething(int a) {
    if (a) {
        cout << "代碼安全:" << a << endl;
    }else {
        throw "參數(shù)出錯";
    }
}

int main() {
    
    try {
        doSomething(0);
    } catch (const char * what) {
        cout << "錯誤描述:" << what << endl;
    }
    
    return 0;
}

注意到throw表達式throw "參數(shù)錯誤"中, "參數(shù)錯誤"被稱為異常對象, 你可以拋出任意的異常對象,不管是int 類型的還是類類型.

而關鍵字catch 后面的小括號中的內(nèi)容表示你想要在此 catch 子句中處理的錯誤類型, 可以帶參數(shù), 那么就可以處理它, 也可以不帶參數(shù),進行統(tǒng)一處理; 當然,你可以寫多個 catch 子句, 處理不同類型的異常對象; 還可以用...表示捕獲所有異常:

#include <iostream>
using namespace std;

class ErrorClass {
public:
    ErrorClass(int a):code(a) {}
    
    void errorDiscription() {
        cout << "錯誤代碼:" << code << endl;
    }
    int code;
};

void doSomething(int a) {
    switch (a) {
        case 0:
            throw "參數(shù)不能為0";
            break;
        case -1:
            throw -1;
        case 1:
            throw ErrorClass(a);
        case -2:
            throw runtime_error("其他異常情況"); 
        default:
            cout << "參數(shù)正常: " << a << endl;
            break;
    }
}

int main() {
    
    try {
        doSomething(-2);
        cout << "如果拋出了異常, 這里將永遠不會被執(zhí)行" << endl;
        
    } catch (const char * what) {
        
        cout << "錯誤描述:" << what << endl;
    } catch (int) {
        
        cout << "int類型的異常對象" << endl;
    } catch (ErrorClass error) {
        
        error.errorDiscription();
        
    } catch (...){ // 捕獲所有異常
        cout << "默認處理其他的異常" << endl;
    }
    
    return 0;
}

可以使用noexcept 說明指定某個函數(shù)不會拋出異常, 但是如果這個用noexcept 指定的函數(shù)實際上拋出了異常, 則程序會直接調(diào)用 terminate 終止程序:

void something() { // 普通函數(shù), 可能會拋出異常
}
void doSomethingButError() noexcept { // 承諾不會拋出異常
    throw exception(); // 違反了異常說明
}

noexcept說明可以帶 bool實參:

void recoup() noexcept(true); // recoup 不會拋出異常
void alloc(int) noexcept(false); // alloc 可能拋出異常

但更常見的是noexcept 說明noexcept 運算符一起使用, noexcept 運算符返回一個 bool 類型:

void g();
void f() noexcept(noexcept(g())); // f 和 g 的異常說明一致, g() 可能拋出異常的話, f() 也可能拋出異常; g()不拋出異常, 則 f()也不會拋出異常.

注意, noexcept 有兩層意義: 當跟在函數(shù)參數(shù)列表后面時,它是異常說明符; 而當作為 noexcept 異常說明的 bool 實參出現(xiàn)時, 它是一個運算符.

Swift 中通過遵循Error 協(xié)議來使任意的類型表示錯誤, 而我們知道在 C++中可以使用繼承,多重繼承和虛繼承來實現(xiàn) Swift 中的協(xié)議.

雖然我們可以任意的對象作為異常對象, 當我們也可以繼承便準庫異常類來自定義我們自己的異常類, 然后拋出自定義異常類來達到同樣的目的.

標準庫異常類的繼承體系

如上圖所示, 在這個標準庫異常類的繼承體系中,層次越低,表示的異常情況就越特殊, 以下我們來設計一個我們自己的異常類:

#include <iostream>
using namespace std;

class my_runtime_error: runtime_error {
public:
    
    // explicit聲明構造函數(shù)時, 它只能使用直接初始化的形式使用,
    // 并且編譯器將不會在自動轉換過程中使用該構造函數(shù)(即不會隱式轉換)
    explicit my_runtime_error(const std::string s): runtime_error(s), str(s) {}
    
    const char *what() {
        return this->str.c_str(); // string 類型轉換成 C 字符串
    }
    
    std::string str;
};

class my_logic_error : logic_error {
public:
    explicit my_logic_error(const std::string s): logic_error(s){}
    
    my_logic_error(const std::string s, const std::string discription): logic_error(s), logicDiscription(discription) {}
    
    const std::string logicDiscription;
};

// 使用我們自定義的異常類
void initSomething() {
    throw my_logic_error("邏輯錯誤:", "此時不應調(diào)用初始化函數(shù)");
}

void runSomething() {
    throw my_runtime_error("運行時錯誤: runSomething()");
}

int main() {
   
    try {
//        initSomething();
        runSomething();
    } catch (my_runtime_error error) {
        cout << error.what() << endl;
        
    } catch (my_logic_error error) {
        cout << error.logicDiscription << endl;
    }
    
    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容