|
Программирование >> Разработка устойчивых систем
вызывают деструкторы, поэтому нормальная зачистка объектов не выполняется (более того, если функция longjmp() выходит за пределы области видимости, в которой должны вызываться деструкторы, это приведет к непредсказуемым последствиям), В результате восстановление после исключительных ситуаций становится практически невозможным, так как позади всегда остаются незачишенные объекты, ставшие недоступными. Следующий пример демонстрирует сказанное для функций setjmp/longjmp: : С01:Nonlocal.срр setjmpO & longjmpO finclude <iostream> #include <csetjmp> using namespace std: class Rainbow { public: RainbowO { cout RainbowO endl: } -RainbowO { cout -RainbowO endl: } jmp buf kansas: void ozO { Rainbow rb: for(int i = 0: i < 3: i++) cout theres no place like home\n : longjmpCkansas. 47): int mainO { if(setjmp(kansas) == 0) { cout tornado, witch, munchkins..An : ozO: } else { cout Auntie Em! I had the strangest dream... endl: } III:- Функция setjmpO вообще ведет себя странно. Если вызывать ее напрямую, она сохраняет всю информацию о текущем состоянии процессора (включая содержимое указателей команд и стека) в аргументе jmp buf и возвращает ноль. В этом случае она ведет себя как обычная функция. Но если вызвать функцию longjmp() с тем же аргументом jmp buf, все выглядит так, словно управление снова только что было возвращено из функции setjmp() - программа начинает выполняться с команды, следующей за вызовом setmp(). Однако на этот раз возвращаемое значение равно второму аргументу 1опщтр(), и это позволяет определить, что в действительности произошел возврат из longjmp(). Нетрудно представить, что при наличии нескольких заполненных буферов jmp buf можно легко перемещаться между разными точками программы. Отличие локальной версии команды перехода по метке (goto) от нелокальной версии состоит в том, что функции setjmp()/longjmp() позволяют вернуться в любую заранее определенную позицию, расположенную выше в стеке (для которой была вызвана функция setjmpO). Запуск исключений Если в программе возникла исключительная ситуация (то есть в текущем контексте не хватает информации для принятия решения о том, как действовать дальше), информацию об ошибке можно передать во внешний, более общий контекст. Для этого в программе создается объект с информацией об исключении, который затем запускается из текущего контекста (говорят, что в программе запускается исключение). Вот как это выглядит: : С01:МуЕггог.срр class MyError { const char* const data: public: MyErrorCconst char* const msg = 0) : data (msg) {} void fO { Запускаем объект исключения: throw MyErrorC something bad happened ): int mainO { Как вскоре будет показано. здесь должен находиться блок try : fO: } III- MyError - обычный класс, конструктор которого в нашем примере получает тип char*. При запуске исключения можно использовать произвольный тип (в том числе и встроенные типы), но обычно для этой цели создаются специальные классы. Ключевое слово throw производит целый ряд полузагадочных манипуляций. Сначала оно создает копию запускаемого объекта и фактически возвращает ее из функции, содержащей выражение throw, даже если тип этого объекта не соответствует типу, который положено возвращать этой функции. Обработку исключений можно упрощенно представить себе как альтернативный механизм возврата (но если зайти с этой аналогией слишком далеко, ничего хорошего не выйдет). Генерируя исключения, вы также можете выходить из обычных областей видимости. В любом случае возвращается значение, а управление из функции или области видимости передается наружу. Возможно, попытка выполнить приведенный пример вас удивит - некоторые компиляторы С++ поддерживают расширенную версию функции longjmpO с уничтожением объектов в стеке. Впрочем, такое поведение зависит от платформы и не является переносимым. Но в С++ возникает проблема: функция longjmpO не думает об объектах; в частности, она не вызывает деструкторы при выходе из области видимости\ Вызовы деструкторов абсолютно необходимы, поэтому такое решение не подходит для С++. Более того, в стандарте С++ сказано, что безусловный вход в область видимости по команде goto (в обход вызова конструкторов) или выход из области видимости с помощью функции LongjmpO при наличии деструктора у объекта в стеке приводит к непредсказуемым последствиям. Перехват исключений 29 На этом все сходство с командой return завершается - точка, в которую происходит возврат, не имеет ничего общего с точкой возврата при обычном вызове функции (управление передается в специальную часть программы, называемую обработчиком исключения; она может находиться далеко от того места, где было запущено исключение). Также уничтожаются все локальные объекты, созданные к моменту запуска исключения. Автоматическое уничтожение локальных объектов часто называется раскруткой стека . Стоит сказать и о том, что в программе могут запускаться объекты исключений разнообразных типов. Как правило, для каждой категории ошибок используется свой тип. Предполагается, что информация будет передаваться как внутри объекта, так и в имени его класса; благодаря этому в контексте вызова можно будет решить, как поступить с исключением. Перехват исключений Как упоминалось ранее, одно из преимуществ механизма обработки исключений С++ состоит в том, что он позволяет программисту сосредоточиться на решаемой задаче, а затем организовать обработку ошибок в другом месте. Блок try Если внутри функции происходит исключение, выполнение этой функции завершается. Если вы не хотите, чтобы команда throw приводила к выходу из функции, создайте внутри функции специальный блок, который должен решать проблемы (а возможно - запускать новые исключения). Этот блок, называемый блоком try, представляет собой обычную область видимости, перед которой ставится ключевое слово try: try { Программный код. который может генерировать исключения Если проверять ошибки по возвращаемому значению функций, вам придется заключать каждый вызов функции между кодом подготовки и кодом проверки даже при многократном вызове одной функции. При обработке исключений выполняемый код помещается в блок try, а обработка исключений производится после блока try. Это существенно упрощает написание и чтение программы, поскольку основной код не смешивается с кодом обработки ошибок. Обработчики исключений Конечно, программа должна где-то среагировать на запущенное исключение. Это место называется обработчиком исключения; в программу необходимо включить обработчик исключения для каждого типа перехватываемого исключения. Тем не менее полиморфизм распространяется и на исключения: один обработчик может перехватывать как определенный тип исключения, так и ис1слючения классов, производных от этого типа. Обработчики исключений следуют сразу же за блоком try и обозначаются ключевым словом catch:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |