|
Программирование >> Поддержка объектно-ориентированного программирования
требовало бы ее перетрансляции, а кроме того препятствовало бы общению с функциями, написанными на других языках. В результате программист стал бы стремиться отключить механизм особых ситуаций и писал бы излишние операторы, чтобы обойти их. Пользователь считал бы такие программы надежными, поскольку мог не заметить подмены, но это было бы совершенно неоправдано. 9.6.1 Неожиданные особые ситуации Если к описанию особых ситуаций относиться не достаточно серьезно, то результатом может быть вызов unexpected(), что нежелательно во всех случая, кроме отладки. Избежать вызова unexpected() можно, если хорошо организовать структуру особых ситуации и описание интерфейса. С другой стороны, вызов unexpected() можно перехватить и сделать его безвредным. Если компонент Y хорошо разработан, все его особые ситуации могут быть только производными одного класса, скажем Yerr. Поэтому, если есть описание class someYerr : public Yerr { /* ... */ }; то функция, описанная как void f() throw (Xerr, Yerr, lOerr); будет передавать любую особую ситуацию типа Yerr вызывающей функции. В частности, обработка особой ситуации типа someYerr в f() сведется к передаче ее вызывающей f() функции. Бывает случаи, когда окончание программы при появлении неожиданной особой ситуации является слишком строгим решением. Допустим функция g() написана для несетевого режима в распределенной системе. Естественно, в g() ничего неизвестно об особых ситуациях, связанных с сетью, поэтому при появлении любой из них вызывается unexpected(). Значит для использования g() в распределенной системе нужно предоставить обработчик сетевых особых ситуаций или переписать g(). Если допустить, что переписать g() невозможно или нежелательно, проблему можно решить, переопределив действие функции unexpected(). Для этого служит функция set unexpected(). Вначале мы определим класс, который позволит нам применить для функций unexpected() метод запроса ресурсов путем инициализации : typedef void(*PFV)(); PFV set unexpected(PFV); class STC { класс для сохранения и восстановления PFV old; функций unexpected() public: STC(PFV f) { old = set unexpected(f); } ~STC() { set unexpected(old); } Теперь мы определим функцию, которая должна в нашем примере заменить unexpected(): void rethrow() { throw; } перезапуск всех сетевых особых ситуаций Наконец, можно дать вариант функции g(), предназначенный для работы в сетевом режиме: void networked g() STC xx(&rethrow); теперь unexpected() вызывает rethrow() g(); В предыдущем разделе было показано, что unexpected() потенциально вызывается из обработчика catch (...). Значит в нашем случае обязательно произойдет повторный запуск особой ситуации. Повторный запуск, когда особая ситуация не запускалась, приводит к вызову terminate(). Поскольку обработчик catch (... ) находится вне той области видимости, в которой была запущена сетевая особая ситуация, бесконечный цикл возникнуть не может. Есть еще одно, довольно опасное, решение, когда на неожиданную особую ситуацию просто закрывают глаза : void muddle on() { cerr << не замечаем особой ситуации\n ; } ... STC xx(&muddle on); теперь действие unexpected() сводится просто к печати сообщения Такое переопределение действия unexpected() позволяет нормально вернуться из функции, обнаружившей неожиданную особую ситуацию. Несмотря на свою очевидную опасность, это решение используется. Например, можно закрыть глаза на особые ситуации в одной части системы и отлаживать другие ее части. Такой подход может быть полезен в процессе отладки и развития системы, перенесенной с языка программирования без особых ситуаций. Все-таки, как правило лучше, если ошибки проявляются как можно раньше. Возможно другое решение, когда вызов unexpected() преобразуется в запуск особой ситуации Fail (неудача): void fail() { throw Fail; } ... STC yy(&fail); При таком решении вызывающая функция не должна подробно разбираться в возможном результате вызываемой функции: эта функции завершится либо успешно (т.е. возвратится нормально), либо неудачно (т.е. запустит Fail). Очевидный недостаток этого решения в том, что не учитывается дополнительная информация, которая может сопровождать особую ситуацию. Впрочем, при необходимости ее можно учесть, если передавать информацию вместе с Fail. 9.7 Неперехваченные особые ситуации Если особая ситуация запущена и не перехвачена , то вызывается функция terminate(). Она же вызывается, когда система поддержки особых ситуаций обнаруживает, что структура стека нарушена, или когда в процессе обработки особой ситуации при раскручивании стека вызывается деструктор, и он пытается завершить свою работу, запустив особую ситуацию. Действие terminate() сводится к выполнению самой последней функции, заданной как параметр для set terminate(): typedef void (*PFV)(); PFV set terminate(PFV); Функция set terminate() возвращает указатель на ту функцию, которая была задана как параметр в предыдущем обращении к ней. Необходимость такой функции как terminate() объясняется тем, что иногда вместо механизма особых ситуаций требуются более грубые приемы. Например, terminate() можно использовать для прекращения процесса, а, возможно, и для повторного запуска системы. Эта функция служит экстренным средством, которое применяется, когда отказала стратегия обработки ошибок, рассчитанная на особые ситуации, и самое время применить стратегию более низкого уровня. Функция unexpected() используется в сходных, но не столь серьезных случаях, а именно, когда функция запустила особую ситуацию, не указанную в ее описании. Действие функции unexpected() сводится к выполнению самой последней функции, заданной как параметр для функции set unexpected(). По умолчанию unexpected() вызывает terminate(), а та, в свою очередь, вызывает функцию abort(). Предполагается, что такое соглашение устроит большинство пользователей. Предполагается, что функция terminate() не возвращается в обратившеюся ней функцию. Напомним, что вызов abort() свидетельствует о ненормальном завершении программы. Для нормального выхода из программы используется функция exit(). Она возвращает значение, которое показывает окружающей системе насколько корректно закончилась программа. 12 = 2; ошибка: i2 здесь невидимо 13 = 3; ошибка: i3 здесь невидимо 9.8 Другие способы обработки ошибок Механизм особых ситуаций нужен для того, чтобы из одной части программы можно было сообщить в другую о возникновении в первой особой ситуации . При этом предполагается, что части программы написаны независимо друг от друга, и в той части, которая обрабатывает особую ситуацию, возможна осмысленная реакция на ошибку. Как же должен быть устроен обработчик особой ситуации? Приведем несколько вариантов: int f(int arg) try { g(arg); catch (x1) { исправить ошибку и повторить g(arg); catch (x2) { произвести вычисления и вернуть результат return 2; catch (x3) { передать ошибку throw; catch (x4) { вместо x4 запустить другую особую ситуацию throw xxii; catch (x5) { исправить ошибку и продолжить со следующего оператора catch (...) { отказ от обработки ошибки terminate(); ... Укажем, что в обработчике доступны переменные из области видимости, содержащей проверяемый блок этого обработчика. Переменные, описанные в других обработчиках или других проверяемых блоках, конечно, недоступны: void f() int i1; try { int i2; catch (x1) { int i3; ... catch (x4) { i1 = 1; нормально
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |