Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга C++.doc
Скачиваний:
24
Добавлен:
10.11.2019
Размер:
2.48 Mб
Скачать

Обработка исключительных ситуаций

Итак, мы с Вами выяснили, что существующие механизмы обработки ошибок не дают однозначного ответа на все вопросы, которые возникают при разработке программного обеспечения. С++ предоставляет новый, улучшенный механизм выявления и обработки ошибок с помощью исключительных ситуаций (проще говоря, исключений). Исключительная ситуация возникает при появлении ошибки во время выполнения программы. Другими словами, у Вас есть возможность не потерять контроль над программой даже при возникновении ошибки. Естественно, чтобы добиться реализации такой возможности, нам необходимо рассмотреть ряд новых терминов и познакомиться с правилами, согласно которым возможна обработка исключений в С++.

Механизм исключительных ситуаций базируется на ключевых словах try (попытаться), throw (вызвать, бросить) и catch (поймать). Рассмотрим механизм исключительных ситуаций. Функция пытается выполнить фрагмент кода. Если в коде содержится ошибка, она с помощью throw генерирует сообщение об ошибке, которое должна поймать (catch) вызывающая функция. Вспомним пример, приведенный в разделе Введение, о нахождении факториала числа и рассмотрим его практическую реализацию.

#include <iostream.h>

int factorial (int);//функция для нахождения факториала числа

void main()

{

//блок try. Вызов функции происходит именно в нем,

//тогда при возникновении оишибки

//гарантировано будет проверен блок catch

//для поимки исключения

try{//объявления блока try

//вызов функции для нахождения факториала числа 5

cout<<"Factorial of 5 is "<<factorial(5)<<endl;

//вызов функции для нахождения факториала числа -3

//вот при этом вызове функции внутри factorial

//возбудится исключение

cout<<"Factorial of -3 is "<<factorial(-3)<<endl;

//до этой строки мы не дойдем, так как предыдущий вызов функции

//генерирует исключение и управление сразу же передается блоку catch

cout<<"Factorial of 7 is "<<factorial(7)<<endl;

}

catch(char* error){//отлавливаем исключения

//так как функция factorial, генерирует исключение с помощью throw и

//использует строку для сообщения об ошибке, то поэтому мы и будем

//отлавливать ошибки типа char*

cout<<"Error was raised... "<<error<<endl;

}

}

int factorial (int n)//функция для нахождения факториала числа

{

//если число отрицательное, генерируем исключение

if (n<0) {

throw "You can't find factorial of negative number ";

}

//здесь 100% гарантия того, что число не является отрицательным

int factor=1;

int i=1;

for (;i<=n;i++){

factor*=i;

}

return factor;

}

Теперь углубимся в понимание механизма обработки исключительных ситуаций. При возникновнии исключения С++ первым делом копирует в памяти сгенерированный объект. Далее, просматривается конец текущего блока try. Если в данном блоке try не будет найден соответсвующий обработчик сгенерированого исключения, то управление передается вызывающей функции, где и осуществляется поиск обработчика, который сможет "принять" данное исключение. Здесь картина повторяется, и если обработчик вновь не найдется, процесс продолжается далее, вверх по стеку вызывающих функций. Если в литературе Вы встретите фразу "разворачивание стека", то речь идет как раз об описанном процессе.

Важной особенностью разворачивания стека является то, что на каждом его этапе все объекты, которые выходят из области видимости, уничтожаются так же, как если бы функция выполнила команду return. Если в результате разворачивания стека не будет найден обработчик соответсвующего исключения - программа аварийно завершается (и пользователи будут обращаться к разработчику). Теперь, естественно, рассмотрим пример, который использует механизм обработки исключительных ситуаций описанный выше.

#include <iostream.h>

void functio1();//первая пробная функция

void functio2(int);//вторая функция, которая

//будет вызываться функцией functio1()

void main()

{

try{

//вызов functio1()

//проверка механизма разворачивания стека для исключений

functio1();

}

//отлавливает ошибки типа char* и int

catch(char* error){

cout<<"Error was raised... "<<error<<endl;

}

catch(int num){

cout<<"Error was raised with code... "<<num<<endl;

}

}

void functio1()

{

int k=7;

cout<<"Inside first function\n";

functio2(k);

}

void functio2(int k)

{

int p=k;

cout<<"Inside second function\n";

//throw "For exception testing only";

throw p;

}

Использование throw без выражения перезапускает полученное исключение. Другими словами, некоторый catch, который перезапукает исключение, не может в силу каких-то причин завершить обработку исключения. Он передает управление в ближайший окружающий блок try, где вызывается обработчик, способный отловить все еще существующее исключение. Выполнение программы продолжится после самого внешнего блока try, который последним обработал перезапущенное исключение.

Итак, Вы уже обратили внимание что может существовать несколько обработчиков catch. Как поступает компилятор в таком случае? Он пытается найти точное соответствие между типом, который был передан при возбуждении исключения и типом аргумента, который обрабатывается catch. У Вас есть также возможность создать обработчик исключений по умолчанию, используя следующий вариат:

catch (...) { //обработчик по умолчанию }

Если компилятор не сможет сопоставить ни один из catch сгенерированому исключению, он вызовет обработчик по умолчанию.