Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C__lab_11.doc
Скачиваний:
3
Добавлен:
12.11.2019
Размер:
266.75 Кб
Скачать

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

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

Теперь в С++ существует встроенный механизм проверок, называемый обработкой исключений. Обработка исключительных ситуаций основана на следующей конструкции:

try // блок контролируемого кода

{

// В блоке при определенной ситуации возбуждается (генерируется)

// исключение оператором throw

}

catch(typeexception varexception)

{// В этом блоке обрабатываются исключения типа typeexception,

// а доступ к исключению осуществляется с помощью переменной

// varexception

}

catch(typeexception)

{// В этом блоке обрабатываются исключения типа typeexception;

// доступа к самому исключению нет

}

catch(...)

{// В этом блоке обрабатываются исключения всех типов

}

Работа механизма обработки исключений состоит в следующем:

1.   если в контролируемом блоке не сгенерировано исключений, то ни один блок catch не выполняется;

2.   если в контролируемом блоке исключение сгенерировано, то выполняется тот блок catch, который в скобках имеет этот тип исключения;

3.   если в контролируемом блоке сгенерировано исключение такого типа, для которого нет соответствующего блока catch, то управление передается блоку catch(...).

Вследствие этого блок catch(...) должен идти последним, поскольку блоки catch, следующие за ним, никогда не будут обработаны. Генерация исключения в контролируемом блоке может осуществляться прямо (в самом блоке вызывается throw) или косвенно (throw вызывается внутри функции выполняющейся в блоке). Во втором случае просмотр кода блока не позволяет увидеть  оператор throw, для этого нужно просматривать тело функции. Можно указать список исключений, которые могут генерироваться функцией. Для этого нужно в конце заголовка функции записать список генерируемых исключений внутри оператора throw: throw(typeexception1, typeexception2,…), например:

int f() throw(typeexception1, typeexception2)

В последнем примере функция f будет генерировать исключения только типов typeexception1 и typeexception2. Данная конструкция позволяет легко контролировать типы исключений, генерируемые функцией.

Многие из функций стандартной библиотеки генерируют исключения одного из стандартных типов. Одним из стандартных типов исключений являются исключения bad_cast (возбуждается при неправильном приведении типов) или bad_alloc (возбуждается при невозможности выделить динамическую память).

Существуют следующие категории стандартных исключений:

1.   исключения языковой поддержки ( bad_alloc, bad_cast, bad_typeid);

2.   исключения стандартной библиотеки С++ (domain_error, invalid_argument, length_error, out_of_range);

3.  исключения внешних ошибок (range_error, overflow_error, underflow_error).

Использование исключений делает программный код проще и более управляемым. Следующая простая программа дает пример использования исключений:

#include <iostream>

#include <new>

using namespace std;

int main()

{

    int *p;

    try {

        p = new int;

    } catch (bad_alloc) {

      cout << "Ошибка выделения памяти\n";

      return 1;

      }

    for(*p = 0; *p < 10; (*p)++)

        cout << *p << " ";

    delete p;

    int n;cin>>n;

    return 0;

}

         В этой программе все достаточно банально, за исключением фрагмента программного кода, в котором происходит выделение памяти для указателя. Вывод программы показан на рисунке. Он свидетельствует о том, что память была нормально выделена и работа программы не нарушалась. Это естественно, поскольку память выделялась под целый тип данных , который занимает мало памяти. Но, когда память выделяется под объект большого класса, то памяти может не хватить и программа не сможет нормально продолжить работу. Поэтому фрагмент кода, в котором происходит выделение памяти всегда нужно помещать в защищенный блок наподобие того, что в программе:

    try {

        p = new myclass;

    } catch (bad_alloc) {

      cout << "Ошибка выделения памяти \n";

      return 1;

      }

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

Рисунок. Демонстрация защищенного блока выделения памяти

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

         p = new myclass;

писать

         p = new(nothrow) myclass;

и использовать проверку указателя на равенство нулю.

         Отметим, что использовать защищенные блоки следует осмотрительно (только в ситуациях действительно возможных катастрофических последствий для программы), не пытаясь подменить обычные управляющие конструкции.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]