在〈捕捉自定义异常〉中自定义了异常类,实际上,C++ 标准程序库在exception
标头定义了基类exception
与一些处理异常的函数,而stdexcept
标头中定义了一系列继承自exception
的异常类:
logic_error
invalid_argument
domain_error
length_error
out_of_range
future_error(C++11)
bad_optional_access(C++17)
runtime_error
range_error
overflow_error
underflow_error
regex_error(C++11)
system_error(C++11)
ios_base::failure(C++11)
filesystem::filesystem_error(C++17)
tx_exception(TM TS)
nonexistent_local_time(C++20)
ambiguous_local_time(C++20)
format_error(C++20)
bad_typeid
bad_cast
bad_any_cast(C++17)
bad_weak_ptr(C++11)
bad_function_call(C++11)
bad_alloc
bad_array_new_length(C++11)
bad_exception
ios_base::failure(until C++11)
bad_variant_access(C++17)
这份清单来自〈std::exception〉,那么该选用哪个呢?〈捕捉自定义异常〉中自定义了InvalidArgument
,似乎可以用invalid_argument
来取代,那么Insufficient
呢?看来没有对应的类,那该继承哪个来自定义异常类呢?
异常若被catch
捕捉,只要catch
处理后没有抛出异常,后续的流程是可以继续的,然而,有些异常就算被catch
捕捉了,最好是别再继续流程,最多就是留下日志(logging),然后令程序崩溃,因为这类异常最好的处理方式,是找出引发异常的代码,直接修正代码,避免重新执行程序再度抛出异常。
例如,内存配置方面的异常bad_alloc
、转型方面的异常bad_cast
等,这些就该是只留下日志、令程序停止,修正代码,而不是在执行时期尝试回复程序的执行流程,在以上异常列表除了logic_error
与其子类之外,其他第一层或其下子类的异常,都是属于这类异常,如果想自定义这类异常,建议继承runtime_error
。
另外有些异常,是属于商务逻辑上的错误范范,例如余额不足,其实是商务逻辑上的考量,这类错误可以继承logic_error
,该类或其子类实例被抛出,是可以捕捉后尝试回复执行流程,例如显示余额不足后,重新请使用者输入提领金额。
(是执行时期错误还是商务逻辑上的错误,有时不见得那么容易分辨,例如,同样是标准程序库提供的异常类,有些语言会将实参错误视为执行时期错误,然而 C++ 是归类在逻辑错误。)
就〈捕捉自定义异常〉中的Insufficient
,可以算是商务逻辑上的错误,可以继承标准程序库的logic_error
来自定义:
class Insufficient : public logic_error {
int balance;
public:
explicit Insufficient(const string &message, int balance)
: logic_error(message), balance(balance) {}
int getBalance() {
return balance;
}
};
而withdraw
、deposit
可以改为:
void Account::deposit(double amount) {
if(amount <= 0) {
throw invalid_argument("必须是正数");
}
this->balance += amount;
}
void Account::withdraw(double amount) {
if(amount <= 0) {
throw invalid_argument("必须是正数");
}
if(amount > this->balance) {
throw Insufficient("余额不足", this->balance);
}
this->balance -= amount;
}
执行时可以如下编写:
Account acct = {"123-456-789", "Justin Lin", 1000};
cout << acct.to_string() << endl;
try {
acct.withdraw(10200);
acct.deposit(-500);
}
catch(invalid_argument &ex) {
cout << "实参错误:" << ex.what() << endl;
}
catch(Insufficient &ex) {
cout << "帐号错误:" << endl
<< "\t" << ex.what() << endl
<< "\t余额 " << ex.getBalance() << endl;
}