C++ Программирование в среде С++ Builder 5

Оператор throw


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

Throw с операндом

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

Листинг 12.1. Программа, демонстрирующая простейшие исключения

////////////////////////////////////////////////////////////////////

// SimpTypes.срр: Перехват простых исключений.

//

#include <iostream.h>

#pragma hdrstop



#include <condefs.h>

int main () (

double d = 1.0;

for (int i=0; i<4; i++) { . try {

cout << endl<< "Entering the try-block..." <<end1;

switch (i) { case 0:

throw "Throwing an exception of char*"; // Выбросить

// строку. case 1:

throw 5; // Выбросить

// целое.

default:

throw d; // Выбросить double. }

// Следующий оператор исполняться не будет

// из-за исключений.

cout<< "In the „try-block after all exceptions..." << endl;

} // Конец try-блока.

catch(int 1) { // Обработчик int.

cout << "Int thrown: " << 1 << endl;

} catch(char* str) { // Обработчик char*.

cout << "String thrown: " << str << endl;

} catch (...) { // Для всего остального.

cout << "An unknown type thrown."<< "Program will.terminate." << endl;

cin.ignore () ;

return -1; // Завершить программу. }

cout<< "End of the loop."<< endl;

} // Конец цикла.


cout << "The End." << endl; // Эти операторы не исполняются cin.ignore (); // никогда, т.к. третье

// исключение

return 0; // завершает программу. }

Вывод программы показан на рис. 12.1

Давайте разберемся, что здесь происходит.

В программе организован цикл, который должен выполниться четыре раза. В нем находится пробный блок, генерирующий исключения различных типов — int, char* и double в зависимости от значения счетчика цикла. На первом проходе оператор throw выбрасывает строку, которая перехватывается вторым по счету обработчиком. Так как обработчик не выполняет никаких действий, кроме вывода сообщения, выполнение про-



Рис. 12.1 Простая программа с исключениями

граммы продолжается с оператора, следующего за списком обработчиков. Цикл продолжается, и при втором входе в пробный блок выбрасывается тип int, перехватываемый первым обработчиком.

На третьем проходе цикла выбрасывается переменная типа double, для которого обработчика не предусмотрено. Однако имеется “всеядный” третий обработчик. Он исполняет оператор return, завершающий программу. Поэтому цикл for в четвертый раз не выполняется и вообще программа не доходит до своего “нормального” конца.

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



Порядок следования catch-обработчиков



Выше мы сказали, что при генерировании исключения ищется обработчик для типа, совпадающего или совместимого с типом исключения. Тут нужна некоторая осторожность. Совместимость в донном случае означает, что:

  • либо тип выброшенного объекта совпадает с типом, ожидаемым обработчиком (если выброшен объект типа Т, то годятся обработчики для Т, const Т, Т& или const T&); ,


  • либо тип обработчика является открытым базовым классом для типа объекта;


  • либо типы обработчика и выброшенного объекта являются указателями, второй из которых может быть приведен к первому согласно общим правилам преобразования указателей.




  • Процедура поиска не ищет “наилучшего соответствия” типов, а просто берет первый по порядку следования подходящий обработчик. Например, у вас есть два класса исключения, причем второй является производным от первого. Если в списке обработчиков первым будет стоять тот, что предназначен для исключений базового класса, он будет перехватывать все исключения — как базового, так и производного классов. Или рассмотрите такой пример:

    int main() {

    try {

    throw "Throwing char*"; // Выбрасывает char*. }

    catch(void*) ( // Ловит void*.

    cout<< "Void* caught." << endl;

    return -1;

    }

    catch(char*) { // Ловит char*.

    cout << "Char* caught." << endl;

    return -1;

    }

    return 0;

    }

    Здесь обработчики исключений расположены в неправильном порядке, так как обработчик для void* будет перехватывать все исключения, предназначенные для обработчика char*.



    Throw без операнда



    Если в операторе throw не указан операнд, то обрабатываемое в данный момент исключение перебрасывается, т. е. поиск подходящих обработчиков будет продолжен далее. Сказанное означает, что такой оператор может применяться только в catch-обработчике или функции, вызываемой из некоторого обработчика.


    Содержание раздела