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
类型返回,如果没有指向任何异常,那会返回nullptr
,rethrow_exception
接受exception_ptr
并重新抛出异常,因此可以在what
中尝试进行对应类型的捕捉。