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

異常處理

|Swift|C++
:-:|:-:|:-:
關(guān)鍵字或類(lèi)型|Error, throws, try, do - catch, try?, defer| throw, try...catch

C++中的異常處理機(jī)制包括:

  • throw 表達(dá)式 異常檢測(cè)部分使用throw 表達(dá)式來(lái)表示它遇到了無(wú)法處理的問(wèn)題. 我們說(shuō)throw 引發(fā)了異常.
  • try 語(yǔ)句塊, 異常處理部分使用try 語(yǔ)句塊處理異常. try 語(yǔ)句塊以關(guān)鍵字 try 開(kāi)始, 并以一個(gè)或多個(gè)catch 子句結(jié)束.
  • 一套異常類(lèi), 用于在 throw 表達(dá)式和相關(guān)的 catch 子句之間傳遞異常的具體信息.

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

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

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

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

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

有多種方式用來(lái)進(jìn)行錯(cuò)誤處理. 一種方式是使用do-catch.在 do代碼塊中, 使用 try來(lái)標(biāo)記可以拋出錯(cuò)誤的代碼. 在catch 代碼塊中,如果沒(méi)有給錯(cuò)誤命名,則默認(rèn)為error:

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

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

可以使用多個(gè)catch 塊來(lái)處理特定的錯(cuò)誤. 就像寫(xiě) switch中的多個(gè)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)
}

練習(xí): 在 do 代碼塊中添加拋出錯(cuò)誤的代碼。你需要拋出哪種錯(cuò)誤來(lái)使第一個(gè) catch 塊進(jìn)行接收?怎么使第二個(gè)和第三個(gè) catch 進(jìn)行接收呢?

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

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

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

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** 的錯(cuò)誤處理由以下三部分組成:

  • thow來(lái)拋出錯(cuò)誤
  • do-catchtry? 來(lái)進(jìn)行錯(cuò)誤處理.
  • Error 錯(cuò)誤協(xié)議

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

在** C++中**也可以使用throw 來(lái)拋出異常:

#include <iostream>
using namespace std;

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

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

如果你運(yùn)行上面的例子, 你會(huì)發(fā)現(xiàn)程序直接崩潰在throw 那行, 這是因?yàn)槌绦虬l(fā)生了異常但沒(méi)有被捕獲,則它將調(diào)用標(biāo)準(zhǔn)庫(kù)函數(shù)terminate終止當(dāng)前程序.

catch子句可以用來(lái)捕獲異常,即 try語(yǔ)句塊:

#include <iostream>
using namespace std;

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

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

注意到throw表達(dá)式throw "參數(shù)錯(cuò)誤"中, "參數(shù)錯(cuò)誤"被稱(chēng)為異常對(duì)象, 你可以拋出任意的異常對(duì)象,不管是int 類(lèi)型的還是類(lèi)類(lèi)型.

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

#include <iostream>
using namespace std;

class ErrorClass {
public:
    ErrorClass(int a):code(a) {}
    
    void errorDiscription() {
        cout << "錯(cuò)誤代碼:" << 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 << "如果拋出了異常, 這里將永遠(yuǎn)不會(huì)被執(zhí)行" << endl;
        
    } catch (const char * what) {
        
        cout << "錯(cuò)誤描述:" << what << endl;
    } catch (int) {
        
        cout << "int類(lèi)型的異常對(duì)象" << endl;
    } catch (ErrorClass error) {
        
        error.errorDiscription();
        
    } catch (...){ // 捕獲所有異常
        cout << "默認(rèn)處理其他的異常" << endl;
    }
    
    return 0;
}

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

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

noexcept說(shuō)明可以帶 bool實(shí)參:

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

但更常見(jiàn)的是noexcept 說(shuō)明noexcept 運(yùn)算符一起使用, noexcept 運(yùn)算符返回一個(gè) bool 類(lèi)型:

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

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

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

雖然我們可以任意的對(duì)象作為異常對(duì)象, 當(dāng)我們也可以繼承便準(zhǔn)庫(kù)異常類(lèi)來(lái)自定義我們自己的異常類(lèi), 然后拋出自定義異常類(lèi)來(lái)達(dá)到同樣的目的.

標(biāo)準(zhǔn)庫(kù)異常類(lèi)的繼承體系

如上圖所示, 在這個(gè)標(biāo)準(zhǔn)庫(kù)異常類(lèi)的繼承體系中,層次越低,表示的異常情況就越特殊, 以下我們來(lái)設(shè)計(jì)一個(gè)我們自己的異常類(lèi):

#include <iostream>
using namespace std;

class my_runtime_error: runtime_error {
public:
    
    // explicit聲明構(gòu)造函數(shù)時(shí), 它只能使用直接初始化的形式使用,
    // 并且編譯器將不會(huì)在自動(dòng)轉(zhuǎn)換過(guò)程中使用該構(gòu)造函數(shù)(即不會(huì)隱式轉(zhuǎn)換)
    explicit my_runtime_error(const std::string s): runtime_error(s), str(s) {}
    
    const char *what() {
        return this->str.c_str(); // string 類(lèi)型轉(zhuǎn)換成 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;
};

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

void runSomething() {
    throw my_runtime_error("運(yùn)行時(shí)錯(cuò)誤: 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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文部分來(lái)自于:代碼鋼琴家blog address:www.cnblogs.com/lulipro/p/75042...
    八目朱勇銘閱讀 1,402評(píng)論 0 4
  • Java異常類(lèi)型 所有異常類(lèi)型都是Throwable的子類(lèi),Throwable把異常分成兩個(gè)不同分支的子類(lèi)Erro...
    予別她閱讀 1,030評(píng)論 0 2
  • 本章將會(huì)介紹 自動(dòng)引用計(jì)數(shù)的工作機(jī)制自動(dòng)引用計(jì)數(shù)實(shí)踐類(lèi)實(shí)例之間的循環(huán)強(qiáng)引用解決實(shí)例之間的循環(huán)強(qiáng)引用閉包引起的循環(huán)強(qiáng)...
    寒橋閱讀 1,029評(píng)論 0 0
  • 有句非常實(shí)在的話,人出來(lái)混,欠下的債總是要還的!就拿十年規(guī)劃作為參考,前五年你瀟灑自在,要雨得雨,要風(fēng)有風(fēng)!后五年...
    活法不止一種閱讀 916評(píng)論 2 1
  • 昨天微信上碰到一位老朋友,她和我聊了很久很多,她特別看重某些東西,所以也看重過(guò)往,非常執(zhí)念,有繞不開(kāi)的心結(jié),我一直...
    清湯寡水bh閱讀 108評(píng)論 1 0

友情鏈接更多精彩內(nèi)容