Анимация
JavaScript
|
Главная Библионтека 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 hand1er(void) { Обработать неожиданное исключение { Готовимся сделать нечто страшное и устанавливаем свой обработчик unexpected function o1d hand1er = set unexpected(my hand1er); Делаем страшное и возвращаем старый обработчик set unexpected(o1d hand1er); Функция-обработчик не может нормально возвращать управление вызывающей программе, если в ней встречается оператор return или при выходе из области действия функции результаты будут неопределенными. Тем не менее, из функции можно запустить исключение и продолжить поиск перехватчика, подходящего для нового исключения. Синтаксис перехвата исключений Чтобы перехватить исключение, поставьте перед блоком ключевое слово try и поместите после него одну или несколько секций catch, которые называются обработчиками (handlers). try { Фрагмент, который может инициировать исключения catch (Exception Type t) { Восстановление после исключения типа Exception Type catch (...) { Восстановление после исключений всех остальных типов Каждый обработчик, за исключением (опять нечаянный каламбур) обработчика с многоточием, соответствует одному конкретному типу ошибок. Если из фрагмента, называемого try-блоком, инициируется исключение, компилятор просматривает список обработчиков в порядке их перечисления и ищет обработчик, подходящий по типу запущенного исключения. Многоточие соответствует исключениям любого типа; если такой обработчик присутствует, он должен находиться последним в списке. Внутри обработчика вы можете предпринимать любые действия для выхода из ситуации. Сведения об исключении можно получить из аргумента catch - кроме обработчика с многоточием, который понятия не имеет, что он должен перехватывать. Выполнение программы после исключения Если выполнение try-блока обходится без исключений, программа благополучно игнорирует все обработчики и продолжает работу с первого выражения за последним обработчиком. Если же исключение все же произошло, оно будет единственным из всего списка, и после его обработки выполнение программы продолжится за последним обработчиком списка. Существуют два исключения (последний нечаянный каламбур): обработчик может содержать вызов крамольного goto или запустить исключение. Если обработчик инициирует исключение, он может продолжить распространение того же исключения или создать новое. catch(int exception) { Сделать что-то, а затем throw("He1p!"); Инициируется исключение типа char* Инициирование исключения из обработчика немедленно завершает выполнение вмещающей функции или блока. Если исключение не перехвачено Если для исключения не найдется ни одного обработчика, по умолчанию вызывается глобальная функция terminate(). Как вы думаете, что она делает? По умолчанию terminate() в конечном счете вызывает библиотечную функцию abort(), и дело кончается аварийным завершением всей программы. Вы можете вмешаться и установить собственную функцию завершения с помощью библиотечной функции set terminate(). Соответствующий фрагмент файла except.h выглядит так: typedef void (*terminate function)(); termination function set terminate(terminate function t func); В строке typedef... объявляется интерфейс к вашей функции завершения. Функция set terminate() устанавливает функцию завершения, которую вместо функции abort() вызывает функция terminate(). Функция set terminate() возвращает текущую функцию завершения, которую позднее можно восстановить повторным вызовом set terminate(). 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [ 17 ] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |