Шаг 5.
Генерация исключений

    На этом шаге мы более детально рассмотрим генерацию исключений.

    Выражение, формирующее исключение, может иметь две формы:

    throw выражение_генерации_исключения; 
    throw;

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

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

    throw;

    Этот оператор, не содержащий выражения после служебного слова, "ретранслирует" уже существующее исключение, т.е. передает его из процедуры обработки и из контролируемого блока, в который входит эта процедура, в процедуру обработки следующего (более высокого) уровня. Естественно, что ретрансляция возможна только для уже созданного исключения. Поэтому оператор throw может использоваться только внутри процедуры обработки исключений и разумен только при вложении контролируемых блоков. В качестве иллюстрации сказанного приведем следующую программу с функцией compare(), анализирующей четность (или нечетность) значения целого параметра. Для четного значения параметра функция формирует исключение типа const char *. Для нечетного значения создается исключение типа int, равное значению параметра. В вызывающей функции GG() - два вложенных контролируемых блока. Во внутреннем - два обработчика исключений. Обработчик catch (int n), приняв исключение, выводит в поток cout сообщение и ретранслирует исключение, т.е. передает его во внешний контролируемый блок. Обработка исключения во внешнем блоке не имеет каких-либо особенностей. Текст программы:

//EXC5_1.СРР - вложение контролируемых блоков
//	и ретрансляция исключений.
#include <iostream.h>
void compare(int k)  // Функция, генерирующая исключения. 
{ 
  if (k%2 != 0) throw k;  // Нечетное значение.
  else throw "Четное";    // Четное значение.
}
// Функция с контролем и обработкой исключений: 
void GG (int j) 
{
  try
   { try { compare(j); } // Вложенный контролируемый блок. 
     catch (int n)
       { cout << "\nНечетное";
         throw;  // Ретрансляция исключения.
       }
     catch (const char  *)   { cout << "\nЧетное"; } 
   }   // Конец внешнего контролируемого блока. 
  // Обработка ретранслированного исключения:
  catch (int i)  {   cout <<"\nРезультат =  " << i;   }  
}// Конец функции GG(). 
void main()  
{
  GG(4);
  GG(7);
}
Текст этой программы можно взять здесь.

    Результат работы программы:

    Четное
    Нечетное
    Результат = 7

    В основной программе функция GG() вызывается дважды - с четным и нечетным параметрами. Для четного параметра 4 функция после печати сообщения "Четное" завершается без выхода из внутреннего контролируемого блока. Для нечетного параметра выполняются две процедуры обработки исключений из двух вложенных контролируемых блоков. Первая из них печатает сообщение "Нечетное" и ретранслирует исключение. Вторая печатает значение нечетного параметра, снабдив его пояснительным текстом: "Результат = 7".

    Если оператор throw; использовать вне контролируемого блока, то вызывается специальная функция terminate(), завершающая выполнение программы.

    При вложении контролируемых блоков исключение, возникшее во внутреннем блоке, последовательно "просматривает" обработчики, переходя от внутреннего (вложенного) блока к внешнему до тех пор, пока не будет найдена подходящая процедура обработки. (Иногда действия по установлению соответствия между процедурой обработки и исключением объясняют в обратном порядке. Говорят.что не исключение просматривает заголовок процедуры обработки, а обработчики анализируют исключение, посланное из контролируемого блока и последовательно проходящее через заголовки обработчиков. Однако это не меняет существа механизма.) Если во всей совокупности обработчиков не будет найден подходящий, то выполняется аварийное завершение программы с выдачей, например, такого сообщения: "Program Aborted". Аналогичная ситуация может возникнуть и при ретрансляции исключения, когда во внешних контролируемых блоках не окажется соответствующей исключению процедуры обработки.

    Используя следующие ниже синтаксические конструкции, можно указывать исключения, которые будет формировать конкретная функция:

    void my_func1() throw(А,В)
        { // Тело функции. } 
    void my_func2() throw()
        { // Тело функции. }

    В первом случае указан список исключений (А и B - это имена некоторых типов), которые может порождать функция my_func1(). Ecли в функции my_func1() создано исключение, отличное по типу от A и B, это будет соответствовать порождению неопределенного исключения и управление будет передано специальной функци unexpected(). По умолчанию функция unexpected() заканчивается вызовом библиотечной функции abort(), которая завершает программу.

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

    На следующем шаге мы более подробно рассмотрим обработку исключений.




Предыдущий шаг Содержание Следующий шаг