重抛异常


throw用来抛出异常,在捕捉到错误时,若要重新抛出错误,也是使用throw;视需求而定,若捕捉错误后,当时情境没有足够的信息可以妥善处理,可就现有信息处理完错误后,重新抛出原错误,或者是收集信息后创建新的错误抛出。

例如,如果你想将invalid_argument也视为一种无法修正的错误,可以在捕捉、进行日志后重新抛出:

try {
    acct.withdraw(10200);
    acct.deposit(-500);
}
catch(invalid_argument &ex) {
    cout << "实参错误:" << ex.what() << endl;
    throw;
}
catch(Insufficient &ex) {
    cout << "帐号错误:"  << endl
         << "\t" << ex.what() << endl
         << "\t余额 " << ex.getBalance() << endl;
}

或者是更具体一些,抽取invalid_argument消息,重新创建runtime_error后抛出:

try {
    acct.withdraw(10200);
    acct.deposit(-500);
}
catch(invalid_argument &ex) {
    cout << "实参错误:" << ex.what() << endl;
    throw runtime_error(ex.what());
}
catch(Insufficient &ex) {
    cout << "帐号错误:"  << endl
         << "\t" << ex.what() << endl
         << "\t余额 " << ex.getBalance() << endl;
}

如果函数或方法确认不会抛出异常,可以使用noexcept指明,例如:

void foo() noexcept {
    ...
}

知道一个函数或方法不会抛出异常,就不用费心去准备try-catch,如果foo函数代码中不经意编写了throw试图抛出异常,编译器会提出警讯,不过编译还是会通过,执行时期若真的抛出了异常,那么程序会直接中断,是否会有异常的传播、try-catch等行为都是不可确定的。

noexcept可以接受表达式,例如noexcept(expr),如果expr结果是true,表示该函数不会抛出异常,若为false,该函数不应抛出异常。

C++ 有个catch(...)语言,并不建议使用,它可以用来捕捉全部类型的异常,捕捉之目的通常是为了留下日志并重新抛出。例如:

try {
    foo();
}
catch(SomeException &e) {
    ...
}
catch(...) {
    cout << "foo 调用发生未处理的错误" << endl;
    throw;
}

或者设置一个处理各种类型错误的函数:

#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;

void what(const exception_ptr &eptr = current_exception()) {
    if (!eptr) { 
        throw bad_exception(); 
    }

    try { 
        rethrow_exception(eptr); 
    }
    catch (const std::exception &e) { 
        cout << e.what() << endl; 
    }
    catch(const string &e) { 
        cout << e << endl;

    }
    catch (const char *e) { 
        cout << e << endl; 
    }
    catch (...) { 
        cout << "unknow error type" << endl;
        throw; 
    }
}

int main() {
    try { 
        throw "XD happens!";
    }
    catch (...) { 
        what();  
    }

    try { 
        throw 42; 
    } catch (...) {
        what();
    }
}

current_exception可以获取目前捕捉到的异常,并以exception_ptr类型返回,如果没有指向任何异常,那会返回nullptrrethrow_exception接受exception_ptr并重新抛出异常,因此可以在what中尝试进行对应类型的捕捉。


展开阅读全文