Обработка исключительных ситуаций
Исключительная ситуация, или исключение, – это возникновение непредвиденного или аварийного события, которое может порождаться некорректным использованием аппаратуры. Например, это деление на ноль или обращение по несуществующему адресу памяти. Обычно эти события приводят к завершению программы с системным сообщением об ошибке. С++ дает программисту возможность восстановить программу и продолжить ее выполнение.
Исключения С++ не поддерживают обработку асинхронных событий, таких как ошибки оборудования или обработку прерываний, например нажатие <Crtl> +<C>. Механизм исключений предназначен только для событий, которые происходят в результате работы самой программы и указываются явным образом.
Исключения позволяют логически разделить вычислительный процесс на две части – обнаружение аварийной ситуации и ее обработку.
Синтаксис исключений
Место, в котором может произойти ошибка, должно входить в контролируемый блок – составной оператор, перед которым записано ключевое слово try:
try {
…
}
Обработка исключения начинается с появления ошибки. Функция, в которой она возникла, генерирует исключение. Для этого используется ключевое слово throw с параметром, определяющим вид исключения. Параметр может быть константой, переменной или объектом и используется для передачи информации об исключении его обработчику:
throw [выражение];
При генерации исключения выполнение текущего блока прекращается, и происходит поиск соответствующего обработчика и передача ему управления. Как правило, генерация происходит не непосредственно в try-блоке, а в функциях, прямо или косвенно в него вложенных.
Не всегда исключение, возникшее во внутреннем блоке, может быть сразу правильно обработано. В этом случае используются вложенные контролируемые блоки, и исключение передается на более высокий уровень с помощью ключевого слова throw без параметров.
Обработчики исключений начинаются со слова catch, за которым в скобках следует тип обрабатываемого исключения. Они должны располагаться непосредственно за try-блоком. Можно записывать один или несколько обработчиков в соответствии с типами обрабатываемых исключений. Синтаксис обработчиков имеет три формы:
Catch (тип имя) {/*тело обработчика */}
Catch (тип ) {/*тело обработчика */}
Catch (…) {/*тело обработчика */}
Первая форма используется, когда имя параметра используется в теле обработчика для выполнения каких-либо действий (например, вывод информации об исключении). Вторая форма не предполагает использования информации об исключении. Многоточие вместо параметра обозначает, что обработчик перехватывает все исключения. Обработчик в этой форме следует помещать после всех остальных.
Пример
catch (int i) {
…
}
catch (const *char) {
…
}
catch (Overflow) {
…
// обработка исключений класса Overflow
}
catch ( j ) {
…
// обработка всех необслуженных исключений
}
После обработки исключения управление передается первому оператору, находящемуся непосредственно за обработчиками исключений. Туда же, минуя код всех обработчиков, передается управление, если исключение в try-блоке не было сгенерировано.
Перехват исключений
Когда с помощью throw генерируется исключение, функции исполнительной библиотеки С++ выполняют следующие действия:
1) создают копию параметра throw в виде статического объекта, который существует, пока исключение не будет обработано;
2) в поисках подходящего обработчика раскручивает стек, вызывая деструкторы локальных объектов, выходящих из области действия;
3) передают объект и управление обработчику, имеющему параметр, совместимый по типу с этим объектом.
При раскручивании стека все обработчики на каждом уровне просматриваются последовательно, от внутреннего блока к внешнему, пока не будет найден подходящий обработчик.
Обработчик считается найденным, если тип объекта, указанного после throw:
- тот же, что и указанный в параметре catch;
- является указателем, который может быть преобразован по стандартным правилам преобразования указателей в тип указателя в параметре cacth.
Из вышеизложенного следует, что обработчики производных классов следует размещать до обработчиков базовых, иначе им никогда не будет передано управление. Обработчик указателя типа void следует размещать после обработчиков указателей конкретного типа.
Пример
# include <fstream.h>
class Hello { // Класс, информирующий о своем создании и уничтожении
public:
Hello ( ) {cout << “Hello!” << endl; }
~Hello ( ) {cout << “Bye!” << endl; }
}
void f1 ( ) {
ifstream ifs (“\\INVALD\FILE\NAME”); // открываем файл
if (!ifs ) {
cout << “Генерируем исключение” << endl;
throw “Ошибка при открытии файла”; }
}
void f2 ( ) {
Hello H; // создаем локальный объект
f1 ( ); // вызываем функцию, генерирующую исключение
}
int main ( ) {
try {
cout << “Входит в try-блок” << endl;
}
catch (int i) {
cout << “Вызван обрабочик int, исключение – ” << I << endl;
return -1;
}
catch (const char *p) {
cout << “Вызван обработчик const char *, исключение - ” << p << endl;
return -1;
}
catch ( j ) {
cout << “Вызван обработчик всех исключений” << endl;
return -1;
}
return 0;
}
Результат выполнения программы:
Входим в try-блок
Hello!
Генерируем исключение
Bye!
Вызван обработчик const char *, исключение – Ошибка при открытии файла
!!! Обратите внимание, что после порождения исключения был вызван деструктор локального объекта, хотя управление из функции f1 было передано обработчику, находящемуся в функции main.
Список исключений
В заголовке функции можно задать список исключений, которые она может прямо или косвенно порождать.
Типы исключений перечисляются в скобках через запятую после ключевого слова throw, расположенного за списком параметров функции, например:
void f1 ( ) throw (int, const char*) {/*Тело функции */}
void f2 ( ) throw (Oops*) {/* */} // Oops* - указатель на класс Oops
Если ключевое слово throw не указано, функция может генерировать любое исключение. Пустой список означает, что функция не должна генериовать исключение:
void f ( ) throw ( ) {
}
Указание списка исключений ни к чему не обязывает – функция может породить исключение, которое она обещала не использовать. Эта ситуация обнаруживается во время исполнения программы и приводит к вызову стандартной функции unexpected, которая по умолчанию вызывает функцию terminate. Функция terminate по умолчанию вызывает функцию abort, которая зварешает выполнение программы.
Иерархии исключений
Исключения могут быть как стандартного, так и определенного пользователем типа. Использование собственных классов исключений предпочтительнее применения стандартных типов данных. С помощью классов можно более гибко организовать передачу информации об исключении, легче дифференцировать обработку исключений, а кроме того, появляется возможность использовать иерархии классов.
Поскольку механизм управления исключениями позволяет создать обработчик для базового класса, родственные исключения можно представить в виде иерархии. Производя исключения от общего базового класса, можно в обработчике перехватывать ссылку или указатель на базовый класс, используя полиморфизм.
Существует ряд стандартных исключений, которые генерируются операциями или функциями С++. Все они являются производными от библиотечного класса exception, описанного в заголовочном файле <stdexcept>. Например, операция new при неудачном выделении памяти генерирует исключение bad_alloc.
Можно определять собственные исключения, производные от стандартных.
Задание
Добавьте в описание “функционального” калькулятора обработку исключительных ситуаций (недопустимый тип данных, операнд, являющийся делителем в операции деления, равен нулю, аргумент логарифма отрицателен или равен нулю и т. д.).
Библиографический список
1. Павловская Т.А. С / C++. Программирование на языке высокого уровня
/ Т.А. Павловская. СПб, 2002.
2. Павловская Т.А. С / C++. Структурное программирование: практикум
/ Т.А.Павловская, Ю.А. Щупак. СПб, 2002.
3. Павловская Т.А. С / C++. Объектно-ориентированное программирование: практикум / Т.А.Павловская, Ю.А.Щупак СПб, 2002.
4. Культин Н.Б. С++ Builder./ Н.Б.Культин. СПб, 2004.
ОГЛАВЛЕНИЕ
ВВЕДЕНИЕ. 2
Классы.. 4
Описание класса. 4
Описание объектов. 6
Указатель this. 7
Конструкторы.. 8
Конструктор копирования. 11
Статические элементы класса. 12
Статические поля. 12
Статические методы.. 13
Дружественные функции и классы.. 14
Дружественные функции. 14
Дружественный класс. 15
Деструкторы.. 16
Перегрузка унарных операций. 18
Перегрузка бинарных операций. 20
Перегрузка операции присваивания. 20
Перегрузка операции приведения типа. 21
Наследование. 22
Ключи доступа. 22
Простое наследование. 23
Правила наследования методов. 27
Виртуальные методы и механизм позднего связывания. 28
Абстрактные классы.. 30
Обработка исключительных ситуаций.. 30
Синтаксис исключений. 31
Перехват исключений. 32
Список исключений. 35
Иерархии исключений. 35
Библиографический список.. 37
ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++