|
Синтаксис инициирования исключений Следующая функция шлепнет вас по рукам, если вызвать ее с неверным параметром. Вместо линейки она воспользуется секцией throw. В этой функции могут произойти две ошибки, представленные константами перечисления Gotcha. enum Gotcha { kTooLow, kTooHigh }; void fn(int x) throw(Gotcha) { if (x < 0) throw kTooLow; // Функция завершается здесь if (x > 1000) throw kTooHigh; // Или здесь // Сделать что-то осмысленное } В первой строке определяется тип исключения. Исключения могут иметь любой тип: целое, перечисление, структура и даже класс. Во второй строке объявляется интерфейс функции с новым придатком - спецификацией исключений, который определяет, какие исключения могут быть получены от функции вызывающей стороной. В данном примере инициируется исключение единственного типа Gotcha. В четвертой и шестой строке показано, как инициируются исключения, которые должны быть экземплярами одного из типов, указанного в спецификации исключений данной функции. Спецификации исключений должны подчиняться следующим правилам. Объявления и определения Спецификация исключений в объявлении функции должна точно совпадать со спецификацией в ее определении. void Fn() throw(int); // Объявление // Где-то в файле .cpp void Fn() throw(int) { // Реализация } Если определение будет отличаться от объявления, компилятор скрестит руки на груди и откажется компилировать определение. Функции без спецификации исключений Если функция не имеет спецификации исключений, она может инициировать любые исключения. Например, следующая функция может инициировать что угодно и когда угодно. void fn(); // Может инициировать исключения любого типа Функции, не инициирующие исключений Если список типов в спецификации пуст, функция не может инициировать никакие исключения. Разумеется, при хорошем стиле программирования эту форму следует использовать всюду, где вы хотите заверить вызывающую сторону в отсутствии инициируемых исключений. void fn() throw(); // Не инициирует исключений Функции, инициирующие исключения нескольких типов В скобках можно указать произвольное количество типов исключений, разделив их запятыми. void fn() throw(int, Exception_Struct, char*); Передача исключений Если за сигнатурой функции не указан ни один тип исключения, функция не генерирует новые исключения, но может передавать дальше исключения, полученные от вызываемых ею функций. void fn() throw; Исключения и сигнатуры функций Спецификация исключений не считается частью сигнатуры функции. Другими словами, нельзя иметь две функции с совпадающим интерфейсом за исключением (нечаянный каламбур!) спецификации исключений. Две следующие функции не могут сосуществовать в программе: void f1(int) throw(); void f1(int) throw(Exception); // Повторяющаяся сигнатура! Спецификация исключений для виртуальных функций В главе 2 мы говорили (точнее, я говорил, а вы слушали) об отличиях между перегрузкой (overloading) и переопределением (overriding). Если виртуальная функция в производном классе объявляется с новой сигнатурой, отсутствующей в базовом классе, эта функция скрывает все одноименные функции базового класса (если вы в чем-то не уверены, вернитесь к соответствующему разделу; это важно понимать). Аналогичный принцип действует и для спецификаций исключений. class Foo { public: virtual Fn() throw(int); }; class Bar : public Foo { public: virtual Fn() throw(char*); // Осторожно! }; Компилятор косо посмотрит на вас, но откомпилирует. В результате тот, кто имеет дело с Foo*, будет ожидать исключения типа int, не зная, что на самом деле он имеет дело с объектом Ваr, инициирующим нечто совершенно иное. Мораль ясна: не изменяйте спецификацию исключений виртуальной функции в производных классах. Только так вам удастся сохранить контракт между клиентами и базовым классом, согласно которому должны инициироваться только исключения определенного типа. Непредусмотренные исключения Если инициированное исключение отсутствует в спецификации исключений внешней функции, программа переформатирует ваш жесткий диск. Шутка. На самом деле она вызывает функцию с именем unexpected(). По умолчанию затем вызывается функция terminate(), о которой будет рассказано ниже, но вы можете сделать так, чтобы вызывалась ваша собственная функция. Соответствующие интерфейсы из заголовочного файла except.h выглядят так: typedef void (*unexpected_function)(); unexpected_function set_unexpected(unexpected_function excpected_func); В строке typedef... объявляется интерфейс к вашей функции. Функция set_unexpected() получает функцию этого типа и организует ее вызов вместо функции по умолчанию. Функция set_unexpected() возвращает текущий обработчик непредусмотренных исключений. Это позволяет временно установить свой обработчик таких исключений, а потом восстановить прежний. В следующем фрагменте показано, как используется этот прием. unexpected_function my_handler(void) { // Обработать неожиданное исключение } { // Готовимся сделать нечто страшное и устанавливаем свой обработчик unexpected_function old_handler = set_unexpected(my_handler); // Делаем страшное и возвращаем старый обработчик set_unexpected(old_handler); } Функция-обработчик не может нормально возвращать управление вызывающей программе, если в ней встречается оператор return или при выходе из области действия функции результаты будут неопределенными. Тем не менее, из функции можно запустить исключение и продолжить поиск перехватчика, подходящего для нового исключения. |
Copyright 2005. Климов Александр. All Right Reserved.