由於程序寫入不正確而發生的異常應該從std::logic_error
派生。這方面的例子是超出界限的索引。 A std::logic_error
是您預期不會發生的錯誤,大多數程序將無法從中恢復。
應該從std::runtime_error
導出可恢復的異常(即由於資源不可用而不能完成的)。
類型的異常應該解釋出了什麼問題。任何可能對開發人員感興趣的輔助信息都可以在what()
字符串中找到。如果您想向開發者提供一連串因果關係,請考慮使用std::throw_with_nested
。這使得感興趣的開發人員可以在不需要通過源代碼的情況下發現他爲什麼操作失敗。
考慮使用例外層次結構。這可以讓您的班級的消費者輕鬆編寫一般性故障條件,同時允許他對重要的個別故障進行測試。
一個人爲的例子:
struct failed_to_start : std::runtime_error {
using std::runtime_error::runtime_error;
};
struct engine_fault : failed_to_start {
using std::failed_to_start::failed_to_start;
};
struct engine_flooded : engine_fault {
using std::engine_fault::engine_fault;
};
struct wrong_key : std::logic_error {
using std::logic_error::logic_error;
};
編輯: 由請求,使用throw_with_nested的全工作實施例(和其他一些有用的技術)
#include <iostream>
#include <string>
#include <stdexcept>
enum class key {
none, mine, yours
};
struct out_of_fuel : std::runtime_error {
using std::runtime_error::runtime_error;
};
struct no_key : std::runtime_error {
using std::runtime_error::runtime_error;
};
struct start_failure : std::runtime_error {
using std::runtime_error::runtime_error;
};
struct wrong_key_error : std::logic_error {
using std::logic_error::logic_error;
};
struct car_configuration_error : std::logic_error {
using std::logic_error::logic_error;
};
struct fuel_tank {
fuel_tank(double initial) : _quantity { initial } {}
void remove_fuel(double amount) {
using namespace std;
if (amount > _quantity) {
throw out_of_fuel { "fuel tank has "s
+ to_string(_quantity)
+ " litres remaining, tried to remove "s
+ to_string(amount) };
}
_quantity -= amount;
}
double _quantity = 0.0;
};
struct ignition {
ignition(key k) : _key_type { k } {}
void insert_key(key k) {
if (_key_type != k) {
throw wrong_key_error { "the wrong key was inserted" };
}
_current_key = k;
}
void turn_key() {
if (_current_key != _key_type) {
throw no_key { "there is no key in the ignition" };
}
}
key _current_key = key::none;
const key _key_type;
};
struct engine {
void run() {
}
};
struct car {
car(key k, double initial_fuel)
: _ignition(k)
, _fuel_tank(initial_fuel)
{}
void start(key k)
try
{
_ignition.insert_key(k);
_ignition.turn_key();
_fuel_tank.remove_fuel(1);
_engine.run();
}
catch(const std::logic_error& e) {
std::throw_with_nested(car_configuration_error { "car configuration error - please check your program" });
}
catch(const std::exception& e) {
std::throw_with_nested(start_failure { "failed to start car" });
}
ignition _ignition;
engine _engine;
fuel_tank _fuel_tank;
};
void print_current_exception(int level = 0);
void print_exception(const std::exception&e, const char* prefix, int level)
{
std::cerr << std::string(level, ' ') << prefix << ": " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
}
catch(const std::exception&) {
print_current_exception(level + 1);
}
}
void print_current_exception(int level)
{
auto eptr = std::current_exception();
if (!eptr)
return;
try {
std::rethrow_exception(eptr);
}
catch(const std::logic_error& e) {
print_exception(e, "logic error", level);
}
catch(const std::runtime_error& e) {
print_exception(e, "runtime error", level);
}
catch(const std::exception& e) {
print_exception(e, "exception", level);
}
}
int main(int argc, const char * argv[])
{
car my_car { key::mine, .05 };
car your_car { key::yours, 100 };
try {
my_car.start(key::mine);
}
catch(const std::exception&) {
print_current_exception();
}
try {
your_car.start(key::mine);
}
catch(const std::exception&) {
print_current_exception();
}
return 0;
}
預期輸出:
runtime error: failed to start car
runtime error: fuel tank has 0.050000 litres remaining, tried to remove 1.000000
logic error: car configuration error - please check your program
logic error: the wrong key was inserted
異常層次結構是可維護的C++程序和庫的基礎。這個問題的答案包含有關接受的最佳實踐的重要信息。這個問題不應該被擱置。 –