Исключения, возникающие из-за неправильной записи программы, должны быть получены из 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;
};
EDIT: по запросу, полный рабочий пример использования 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++. Ответы на этот вопрос содержат важную информацию о принятой передовой практике. Вопрос не должен быть приостановлен. –