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

// Использование класса исключений.

#include

#include

using namespace std;

class MyException {

 public:

  char str_what[80];

  MyException() { *str_what =0; }

  MyException(char *s) { strcpy(str_what, s);}

};

int main()

{

 int a, b;

 try {

  cout << "Введите числитель и знаменатель: ";

   cin >> а >> b;

  if( !b) throw MyException("Делить на нуль нельзя!");

  else

   cout << "Частное равно " << a/b << "\n";

 }

 catch (MyException e) {

  // перехват ошибки

  cout << e.str_what << "\n";

 }

 return 0;

}

Вот один из возможных результатов выполнения этой программы.

Введите числитель и знаменатель: 10 0

Делить на нуль нельзя!

После запуска программы пользователю предлагается ввести числитель и знаменатель. Если знаменатель равен нулю, создается объект класса MyException, который содержит информацию о попытке деления на нуль. Таким образом, класс MyException инкапсулирует информацию об ошибке, которая затем используется обработчиком исключений для уведомления пользователя о случившемся.

Безусловно, реальные классы исключений гораздо сложнее класса MyException. Как правило, создание классов исключений имеет смысл в том случае, если они инкапсулируют информацию, которая бы позволила обработчику исключений эффективно справиться с ошибкой и по возможности восстановить работоспособность программы.

Использование нескольких catch-инструкций

Как упоминалось выше, с try-блоком можно связывать не одну, а несколько catch-инструкций. В действительности именно такая практика и является обычной. Но при этом все catch-инструкции должны перехватывать исключения различных типов. Например, в приведенной ниже программе обеспечивается перехват как целых чисел, так и указателей на символы.

#include

using namespace std;

// Здесь возможен перехват исключений различных типов.

void Xhandler(int test)

{

 try {

  if(test) throw test;

  else throw "Значение равно нулю.";

 }

 catch (int i) {

 cout << "Перехват! Исключение №: " << i << '\n';

 }

 catch(char *str) {

  cout << "Перехват строки: ";

  cout << str << '\n';

 }

}

int main()

{

 cout << "НАЧАЛО\n";

 Xhandler(1);

 Xhandler(2);

 Xhandler(0);

 Xhandler(3);

 cout << "КОНЕЦ";

 return 0;

}

Эта программа генерирует такие результаты.

НАЧАЛО

Перехват! Исключение №: 1

Перехват! Исключение №: 2

Перехват строки: Значение равно нулю.

Перехват! Исключение №: 3

КОНЕЦ

Как видите, каждая catch-инструкция отвечает только за исключение "своего" типа. В общем случае catch-выражения проверяются в порядке следования, и выполняется только тот catch-блок, в котором тип заданного исключения совпадает с типом сгенерированного исключения. Все остальные catch-блоки игнорируются.

Перехват исключений базового класса

Важно понимать, как выполняются catch-инструкции, связанные с производными классами. Дело в том, что catch-выражение для базового класса "отреагирует совпадением" на исключение любого производного типа (т.е. типа, выведенного из этого базового класса). Следовательно, если нужно перехватывать исключения как базового, так и производного типов, в catch-последовательности catch-инструкцию для производного типа необходимо поместить перед catch-инструкцией для базового типа. В противном случае catch-выражение для базового класса будет перехватывать (помимо "своих") и исключения всех производных классов. Рассмотрим, например, следующую программу:

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

#include

using namespace std;

class В {

};

class D: public В {

};

int main()

{

Перейти на страницу:

Все книги серии Изучайте C++ с профессионалами

Похожие книги