Вы заметили ошибку? Такие ошибки трудно выявить, так как сам код является правильным: ошибка заключается в том, что программист не включил в него проверку.
ПОПРОБУЙТЕ
Выполните эту программу при разных значениях. Выведите на печать значения переменных area1
, area2
, area3
и ratio
. Вставьте в программу больше проверок разных ошибок. Вы уверены, что перехватите все ошибки? Это вопрос без подвоха; в данном конкретном примере можно ввести правильный аргумент и перехватить все возможные ошибки.
Существует другой способ решить описанную проблему: использовать исключения (exceptions).
5.6. Исключения
Как и в большинстве языков программирования, в языке С++ существует механизм обработки ошибок: исключения. Основная идея этого понятия заключается в отделении выявления ошибки (это можно сделать в вызываемой функции) от ее обработки (это можно сделать в вызывающей функции), чтобы гарантировать, что ни одна выявленная ошибка не останется необработанной. Иначе говоря, исключения создают механизм, позволяющий сочетать наилучшие подходы к обработке ошибок, исследованные нами до сих пор. Какой бы легкой ни была обработка ошибок, исключения сделают ее еще легче.
Основная идея заключается в следующем: если функция обнаруживает ошибку, которую не может обработать, она не выполняет оператор return
как обычно, а генерирует исключение с помощью оператора throw
, показывая, что произошло нечто неправильное.
Любая функция, прямо или косвенно вызывающая данную функцию, может перехватить созданное исключение с помощью оператора catch
, т.е. указать, что следует делать, если вызываемый код использовал оператор throw
. Функция расставляет ловушки для исключения с помощью блока try
(мы опишем его в следующих разделах), перечисляя виды исключений, которые она хочет обработать в своих разделах catch
блока try
. Если ни одна из вызывающих функций не перехватила исключение, то программа прекращает работу.
Мы еще вернемся к исключениям позже (в главе 19), чтобы использовать их немного более сложным способом.
5.6.1. Неправильные аргументы
Рассмотрим вариант функции area()
, использующий исключения.
class Bad_area { }; // Тип, созданный специально для сообщений
// об ошибках,
// возникших в функции area()
// Вычисляет площадь прямоугольника;
// при неправильном аргументе генерирует исключение Bad_area
int area(int length, int width)
{
if (length<=0 || width<=0) throw Bad_area();
return length*width;
}
Иначе говоря, если аргументы правильные, то программа всегда возвращает площадь прямоугольника, а если нет, то выходим из функции area()
с помощью оператора throw
, надеясь найти ответ в одном из разделов catch
. Bad_area
— это новый тип, предназначенный исключительно для генерирования исключений в функции area()
, так, чтобы один из разделов catch
распознал его как исключение, сгенерированное функцией area()
. Типы, определенные пользователями (классы и перечисления), обсуждаются в главе 9. Обозначение Bad_area()
означает “Создать объект типа Bad_area”, а выражение throw Bad_area()
означает “Создать объект типа Bad_area
и передать его (throw
) дальше”.
Теперь функцию можно написать так:
int main()
try {
int x = –1;
int y = 2;
int z = 4;
// ...
int area1 = area(x,y);
int area2 = framed_area(1,z);
int area3 = framed_area(y,z);
double ratio = area1/area3;
}
catch (Bad_area) {
cout << "Ой! Неправильный аргумент функции area()\n";
}
Во-первых, этот фрагмент программы обрабатывает все вызовы функции area()
как вызов из модуля main()
, так и два вызова из функции framed_area()
. Во-вторых, обработка ошибки четко отделена от ее выявления: функция main()
ничего не знает о том, какая функция выполнила инструкцию throw Bad_area()
, а функция area()
ничего не знает о том, какая функция (если такая существует) должна перехватывать исключения Bad_area
, которые она генерирует. Это разделение особенно важно в крупных программах, написанных с помощью многочисленных библиотек. В таких программах ни один человек не может обработать ошибку, просто поместив некоторый код в нужное место, поскольку никто не может модифицировать код одновременно в приложении и во всех библиотеках.
5.6.2. Ошибки, связанные с диапазоном