Обработка исключительных ситуаций
5.1. Механизм обработки исключений в С++ Рассмотрим программы, которые используют библиотеки классов. Разработчик библиотеки может предложить методы выявления ошибок, возникающих на этапе выполнения программы. Например, в классе array перегружена операция [] для проверки принадлежности индекса диапазону. Выявить такие ошибки можно только на этапе выполнения программы, и разработчик библиотеки знает, как это сделать. Однако он не знает, что делать дальше, поскольку об этом знает только разработчик программы, использующий эту библиотеку. С другой стороны, разработчик программы не знает, как найти эти ошибки, а даже если и знает, то это требует введения в программу специальных фрагментов, осуществляющих поиск ошибок, что приводит к ее усложнению и ухудшению читабельности. В языке С++ вводится понятие исключения (exception), которое использует специальный механизм для выявления и устранения ошибок рассмотренного типа. Для реализации механизма обработки исключений в языке С++ введены следующие три ключевых слова: try (попытка контролировать), catch (ловить), throw (бросать, кидать, генерировать). Служебное слово try позволяет выделить в любом месте программы так называемый контролируемый блок: try{операторы} Среди операторов могут быть описания, определения, обычные операторы С++ и специальные операторы генерации исключения: throw выражение_генерации_исключения Когда выполняется такой оператор, то с помощью выражения после throw формируется специальный объект, называемый исключением. Исключение создается как статический объект, тип которого определяется типом выражения. После формирования исключения throw передает исключение и управление непосредственно за границы контролируемого блока. В этом месте (за закрывающей фигурной скобкой) обязательно находится один или несколько обработчиков исключений:
catch(тип_исключения имя){операторы} Обработчик исключений похож на определение функции с одним параметром. Когда обработчиков несколько, они должны отличаться друг от друга типами исключений. Это похоже на перегрузку функций. Механизм обработки исключений является весьма общим средством управления программой. Он может использоваться не только при обработке аварийных ситуаций, но и любых других состояний в программе, которые почему-либо выделил программист. Для этого достаточно, чтобы та часть программы, где планируется возникновение исключений, была оформлена в виде контролируемого блока, в котором выполнялись бы операторы генерации исключений при обнаружении заранее запланированных ситуаций. Общую идею обработки исключений неформально можно выразить следующим образом: а) программа пользователя try{ // try-блок /* Попытайтесь делать что-то и если не возникают какие-то особые ситуации, то продолжайте в том же духе. Если возникает особая ситуация, то выполнение операций прерывается и осуществляется автоматический переход к catch-блоку */ } catch(…){ /* Обработка исключительной ситуации */ }
б) библиотечная программа (библиотечный класс) Обычное выполнение программы, если не возникает особая ситуация. Если возникает заранее определенная особая ситуация, то выполняется переход throw к обработке этой ситуации, вид которой определяется именем класса после слова throw. В целом механизм обработки исключений представляет собой альтернативу традиционным методам, таким как: · завершение программы; · возвращение значения, указывающего на ошибку; · установление некоторого кода ошибки (глобальная переменная errno в C); · вызов некоторой функции, которая обрабатывает ошибку.
Но эти методы работают хуже. Техника обработки исключений позволяет выделить в программе отдельные независимые части: собственно программу (try-блок) и фрагмент для обработки ошибок (catch-блок). Это делает программу более читабельной и регулярной (нехаотичной). Этот механизм поддерживает хороший стиль программирования, т.к. плохая программа будет периодически прерываться (завершаться), а не давать ошибочные результаты. Следует указать, что рассмотренный механизм применим только для выявления синхронных исключений, т.е. таких, которые можно сопоставить с выполнением различных действий в самой программе. Асинхронные исключения, генерируемые, например, аппаратно (сигналы от клавиатуры, таймера и так далее), нельзя непосредственно обработать этим способом. Пример 5.1.1. Содержит локальный класс – индикатор исключения range. #include <iostream.h> #include<windows.h> #define ss 10 class string {char *p; int size; public: string(int SIZE){p=new char[size=SIZE];} ~string(){delete[] p;} //локальный класс – класс индикатор исключения class range {/*здесь могут быть поля и методы */}; char& operator[](int); //опреция «доступ по индексу }; char& string:: operator[](int j) { /* проверяем, не вышел ли индекс за границы и если вышел, то генерируем исключение–создаем объект класса исключения */ if((j<0)||(j>=size)) throw range(); return p[j]; } void main () { SetConsoleOutputCP(1251); //Меняем кодовую страницу int index; string str(ss); //заполняем строку символами for(int i=0;i<ss;i++) str[i]=’A’+i; Try {for(;;) //бесконечный цикл {cout<<”\nВведите индекс: ”; cin>>index; cout<<str[index]; } } catch (string:: range){cout<<”\nОшибка!”;} } Здесь обработчик исключения позволяет завершить при необходимости или при ошибке бесконечный цикл. Пример 5.1.2. Содержит глобальный класс – индикатор исключения range. #include <iostream.h> #include<windows.h> class range {}; class string {char *p; int size; public: string(int SIZE){p=new char[size=SIZE];} ~string(){delete[] p;} char& operator[](int); }; char& string:: operator[](int j) {if((j<0)||(j>=size)) throw::range(); return p[j]; } void main () { void main () { SetConsoleOutputCP(1251); int index; string str(ss); //заполняем строку символами for(int i=0;i<ss;i++) str[i]=’A’+i; Try {for(;;) //бесконечный цикл {cout<<”\nВведите индекс: ”; cin>>index; cout<<str[index]; } } catch (range){cout<<”\nОшибка!”;} } } Пример 5.1.3. Используем два обработчика исключений и два индикатора исключений, один из которых глобальный, а другой – локальный.
Исключение типа локального класса range генерируется, когда индекс выходит за границу. Исключение типа глобального класса range генерируется, когда количество ошибок больше заданного (в нашем примере больше 2). #include <iostream.h> #include <stdlib.h> #include<windows.h> #define ss 10 class range {}; // глобальный класс – индикатор исключения range. class string {char *p; int size; int i; //счетчик ошибок public: string(int SIZE):i(0){ p=new char[size=SIZE];} class range {}; //локальный класс–индикатор исключения range. ~string(){delete[] p;} char& operator[](int);}; //конец определения string char& string:: operator[](int j) { if((j<0)||(j>=size)){i++; if(i>2) throw:: range(); else throw range();} return p[j]; } void main () { SetConsoleOutputCP(1251); int index; string str(ss); //заполняем строку символами for(int i=0;i<ss;i++) str[i]=’A’+i; for(;;) { try {for(;;){for(;;) //бесконечный цикл { cout<<”\nВведите индекс: ”; cin>>index; cout<<str[index]; }}} catch (range) //первый обработчик. {cout<<”\nСлишком много ошибок!”; exit(0); } catch (string:: range) //второй обработчик. {cout<<”\nИндекс за границей!”; cout<<”\nПопытайтесь снова”<<endl; } } } Здесь при активизации первого обработчика исключений осуществляется завершение программы по exit. При активизации второго обработчика после его завершения выполняется очередная итерация бесконечного цикла. Пример 5.1.4. Покажем возможный вызов в обработчике исключения функции, содержащей блоки try и catch. Это можно делать, например, когда возникшая ошибка не является опасной. В результате ее можно исправить и повторить соответствующие операции.
#include <iostream.h> #include<stdio.h> #include<windows.h> #include<process.h> #define ss 20 class string { char *p; int size; public: string(int SIZE){p=new char[size=SIZE];} ~string(){delete[] p;} class range{/*здесь могут быть поля и методы */}; char& operator[](int); }; char& string::operator[](int j) { if((j<0)||(j>=size)) throw range(); return p[j]; } void f() //рекурсивная функция { string str(ss); for(int i=0;i<ss;i++) str[i]='A'+i; int index; try{ cout<<"\nВведите индекс: "; cin>>index; cout<<str[index]; } catch(string::range) {cout<<"\nОшибка!"; f(); //новая попытка } } int main() { SetConsoleOutputCP(1251); int k; char s[]="\nПродолжить? Да-1,Нет-0 ";
while(true) { f(); cout<<s; cin>>k; if(k==0){system("pause"); return 0;} }
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|