Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Для примера вернемся к программе раздела 1.5.2, в которой суммируются два объекта класса Sales_item. Она проверяет, относятся ли обе прочитанные записи к одной книге. Если нет, она отображает сообщение об ошибке и завершает работу.
Sales_item item1, item2;
cin >> item1 >> item2;
// сначала проверить, представляют ли объекты item1 и item2
// одну и ту же книгу
if (item1.isbn() == item2.isbn()) {
cout << item1 + item2 << endl;
return 0; // свидетельство успеха
} else {
cerr << "Data must refer to same ISBN"
<< endl;
return -1; // свидетельство отказа
}
В более реалистичной программе суммирующая объекты часть могла бы быть отделена от части, обеспечивающей взаимодействие с пользователем. В таком случае проверяющую часть можно было бы переписать так, чтобы она передавала исключение, а не возвращала свидетельство отказа.
// сначала проверить, представляют ли объекты item1 и item2
// одну и ту же книгу
if (item1.isbn() != item2.isbn())
throw runtime_error("Data must refer to same ISBN");
// если управление здесь, значит, ISBN совпадают
cout << item1 + item2 << endl;
Если теперь ISBN окажутся разными, будет передан объект исключения типа runtime_error. Передача исключения завершает работу текущей функции и передает управление обработчику, способному справиться с этой ошибкой.
Тип runtime_error является одним из типов исключения, определенных в заголовке stdexcept стандартной библиотеки. Более подробная информация по этой теме приведена в разделе 5.6.3. Объект класса runtime_error следует инициализировать объектом класса string или символьной строкой в стиле С (см. раздел 3.5.4). Эта строка представляет дополнительную информацию о проблеме.
5.6.2. Блок try
Блок try имеет следующий синтаксис:
try {
операторы_программы
} catch (объявление_исключения) {
операторы_обработчика
} catch (объявление_исключения) {
операторы_обработчика
} // ...
Блок try начинается с ключевого слова try, за которым следует блок кода, заключенный в фигурные скобки.
Блок try сопровождается одним или несколькими блоками catch. Блок catch состоит из трех частей: ключевого слова catch, объявления (возможно, безымянного) объекта в круглых скобках (называется объявлением исключения (exception declaration)) и операторного блока. Когда объявление исключения в блоке catch совпадает с исключением, выполняется связанный с ним блок. По завершении выполнения кода обработчика управление переходит к оператору, следующему непосредственно после него.
Операторы_программы в блоке try являются обычными программными операторами, реализующими ее логику. Подобно любым другим блокам кода, блоки try способны содержать любые операторы языка С++, включая объявления. Объявленные в блоке try переменные недоступны вне блока, в частности, они не доступны в блоках catch.
Создание обработчикаВ приведенном выше примере, чтобы избежать суммирования двух объектов класса Sales_item, представляющих разные книги, использовался оператор throw. Предположим, что суммирующая объекты класса Sales_item часть программы отделена от части, взаимодействующей с пользователем. Эта часть могла бы содержать примерно такой код обработки исключения, переданного в блоке сложения.
while (cin >> item1 >> item2) {
try {
// код, который складывает два объекта класса Sales_item
// если при сложении произойдет сбой, код передаст
// исключение runtime_error
} catch (runtime_error err) {
// напомнить пользователю, что ISBN слагаемых объектов
// должны совпадать
cout << err.what()
<< "nTry Again? Enter y or n" << endl;
char c;
cin >> с;
if (!cin || с == 'n')
break; // выход из цикла while
}
}
В блоке try расположена обычная логика программы. Это сделано потому, что данная часть программы способна передать исключение типа runtime_error.
Данный блок try обладает одним разделом catch, который обрабатывает исключение типа runtime_error. Операторы в блоке после ключевого слова catch определяют действия, выполняемые в случае, если код в блоке try передаст исключение runtime_error. В данном случае обработка подразумевает отображение сообщения об ошибке и запрос у пользователя разрешения на продолжение. Когда пользователь вводит символ 'n', цикл while завершается, в противном случае он продолжается и считывает два новых объекта класса Sales_item.
В сообщении об ошибке используется текст, возвращенный функцией err.what(). Поскольку известно, что классом объекта исключения err является runtime_error, нетрудно догадаться, что функция what() является членом (см. раздел 1.5.2) класса runtime_error. В каждом из библиотечных классов исключений определена функция-член what(), которая не получает никаких аргументов и возвращает символьную строку в стиле С (т.е. const char*). В случае класса runtime_error эта строка является копией строки, использованной при инициализации объекта класса runtime_error. Если описанный в предыдущем разделе код передаст исключение, то отображенное разделом catch сообщение об ошибке будет иметь следующий вид:
Data must refer to same ISBN
Try Again? Enter y or n
При поиске обработчика выполнение функций прерываетсяВ сложных системах программа может пройти через несколько блоков try прежде, чем встретится с кодом, который передает исключение. Например, в блоке try может быть вызвана функция, в блоке try которой содержится вызов другой функции с ее собственным блоком try, и т.д.
Поиск обработчика осуществляется по цепочке обращений в обратном порядке. Сначала поиск обработчика исключения осуществляется в той функции, в которой оно было передано. Если соответствующего раздела catch не найдено, работа функции завершается, а поиск продолжается в той функции, которая вызвала функцию, в которой было передано исключение. Если и здесь соответствующий раздел catch не найден, эта функция также завершается, а поиск продолжается по цепочке вызовов дальше, пока обработчик исключения соответствующего типа не будет найден.
Если соответствующий раздел catch так и не будет найден, управление перейдет к библиотечной функции terminate(), которая определена в заголовке exception. Поведение этой функции зависит от системы, но обычно она завершает выполнение программы.
Исключения, которые были переданы в программах, не имеющих блоков try, обрабатываются аналогично: в конце концов, без блоков try не может быть никаких обработчиков и ни для каких исключений, которые, однако, вполне могут быть переданы. В таком случае исключение приводит к вызову функции terminate(), которая (как правило) и завершает работу программы.
Внимание! Написание устойчивого к исключениям кода — довольно сложная задачаВажно понимать, что исключения прерывают нормальный поток программы. В месте, где происходит исключение, некоторые из действий, ожидаемых вызывающей стороной, могут быть выполнены, а другие нет. Как правило, пропуск части программы может означать, что объект останется в недопустимом или неполном состоянии, либо что ресурс не будет освобожден и т.д. Программы, которые правильно "зачищают" объекты во время обработки исключений, называют устойчивыми к исключениям (exception safe). Написание устойчивого к исключениям кода чрезвычайно сложно и практически не рассматривается в данном вводном курсе.
Некоторые программы используют исключения просто для завершения программы в случае проблем. Такие программы вообще не заботятся об устойчивости к исключениям.
Программы, которые действительно обрабатывают исключения и продолжают работу, должны постоянно знать, какое исключение может произойти и что программа должна делать для гарантии допустимости объектов, невозможности утечки ресурсов и восстановления программы в корректном состоянии.
Некоторые из наиболее популярных методик обеспечения устойчивости к исключениям здесь будут упомянуты. Однако читатели, программы которых требуют надежной обработки исключений, должны знать, что рассматриваемых здесь методик недостаточно для полного обеспечения устойчивости к исключениям.