Существует несколько возможных причин сбоя при вводе данных. Одна из них — тип данных, которые мы пытаемся считать, — отличается от типа
double
. На ранних стадиях разработки мы часто хотим просто сообщить, что нашли ошибку и прекратить выполнение программы, потому что еще не знаем, как на нее реагировать. Иногда мы впоследствии возвращаемся к этому фрагменту и уточняем свои действия. Рассмотрим пример.
double some_function
{
double d = 0;
cin >> d;
if (!cin)
error("невозможно
считать число double в 'some_function'");
// делаем что-то полезное
}
Строку, переданную функции
error
, можно вывести на печать для облегчения отладки или для передачи сообщения пользователю. Как написать функцию
error
так, чтобы она оказалась полезной для многих программ? Она не может возвращать никакого значения, потому что неизвестно, что с ним делать дальше. Вместо этого лучше, чтобы функция
error
прекращала выполнение программы после получения сообщения об ошибке. Кроме того, перед выходом иногда следует выполнить определенные несложные действия, например, оставить окно с сообщением активным достаточно долго, чтобы пользователь мог прочесть сообщение. Все это вполне естественно для исключений (подробнее об этом — в разделе 7.3).
В стандартной библиотеке определено несколько типов исключений, таких как
out_of_range
, генерируемых классом
vector
. Кроме того, в этой библиотеке есть исключение
runtime_error
, идеально подходящее для наших нужд, поскольку в ней хранится строка, которую может использовать обработчик ошибки.
Итак, нашу простую функцию
error
можно переписать следующим образом:
void error(string s)
{
throw runtime_error(s);
}
Когда нам потребуется поработать с исключением
runtime_error
, мы просто перехватим его. Для простых программ перехват исключения
runtime_error
в функции
main
является идеальным.
int main
try {
// наша программа
return 0; // 0 означает успех
}
catch (runtime_error& e) {
cerr << "runtime error: " << e.what << '\n';
keep_window_open;
return 1; // 1 означает сбой
}
Вызов
e.what
извлекает сообщение об ошибке из исключения
runtime_error
.
Символ
&
в выражении
catch(runtime_error& e) {
означает, что мы хотим передать исключение по ссылке. Пожалуйста, пока рассматривайте это выражение просто как техническую подробность. В разделах 8.5.4–8.5.6
мы объясним, что означает передача сущности по ссылке.
Обратите внимание на то, что для выдачи сообщений об ошибках мы использовали поток
cerr
. Этот поток очень похож на поток
cout
за исключением того, что он предназначен для вывода сообщений об ошибках. По умолчанию потоки
cerr
и
cout
выводят данные на экран, но поток
cerr
не оптимизирован, поэтому более устойчив к ошибкам и в некоторых операционных системах может быть перенаправлен на другую цель, например на файл. Используя поток
cerr
, можно документировать ошибки. Именно поэтому для вывода ошибок мы используем поток
cerr
.
Исключение
out_of_range
отличается от исключения
runtime_error
, поэтому перехват исключения
runtime_error
не приводит к обработке ошибок
out_of_range
, которые могут возникнуть при неправильном использовании класса
vector
или других контейнерных типов из стандартной библиотеки. Однако и
out_of_range
, и
runtime_error
являются исключениями, поэтому для работы с ними необходимо предусмотреть перехват объекта класса exception.
int main
try {
// наша программа
return 0; // 0 означает успех
}
catch (exception& e) {
cerr << "error: " << e.what << '\n';
keep_window_open;
return 1; // 1 означает сбой
}
catch (...) {
cerr << "Ой: неизвестное исключение !\n";
keep_window_open;
return 2; // 2 означает сбой
}
Здесь, для того чтобы перехватить все исключения, мы добавили инструкцию
catch(...)
.
Когда исключения обоих типов (
out_of_range
и
runtime_error
) рассматриваются как разновидности одного и того же типа
exception
, говорят, что тип exception является базовым типом (супертипом) для них обоих. Этот исключительно полезный и мощный механизм будет описан в главах 13–16.
Снова обращаем ваше внимание на то, что значение, возвращаемое функцией
main
, передается системе, вызвавшей программу. Некоторые системы (такие как Unix) часто используют это значения, а другие (такие как Windows), как правило, игнорируют их. Нуль означает, что программа завершилась успешно, а ненулевое значение, возвращенное функцией
main
, означает какой-то сбой.
При использовании функции
error
для описания возникшей проблемы часто необходимо передать две порции информации. В данном случае эти две порции просто объединяются в одну строку. Этот прием настолько широко распространен, что мы решили представить его в виде второго варианта функции