|
Программирование >> Оптимизация возвращаемого значения
* В июле 1995 года комитет стандартов по С++ ISO/ANSI добавил в спецификацию языка функцию uncaught exception, возвращающую true, если исключение возникло, но не было обработано. Правило 11. Не распространяйте обработку исключений за пределы деструктора Деструктор может быть вызван в двух ситуациях. Во-первых, при нормальных условиях, то есть когда объект покидает область видимости или явно вызван оператор delete. Во-вторых - когда объект удаляется механизмом обработки исключений во время выравнивания стека. Таким образом, в момент вызова деструктора исключение уже может быть сгенерировано, а может и не быть. К сожалению, при исполнении кода деструктора определить нельзя*. Поэтому приходится писать деструкторы, исходя из пессимистического предположения, что исключение уже возникло; ведь если деструктор при уже имеющемся исключении также генерирует исключение и управление передается в вызывающий модуль, то C+-I- запускает функцию terminate. Действие этой функции полностью соответствует ее имени: она завершает выполнение программы. Более того, выполнение программы заканчивается немедленно, не удаляются даже локальные объекты. Рассмотрим в качестве примера класс Session, обеспечивающий обработку подключения к компьютерной сети, то есть событий, которые происходят между моментом подключения и отключения. Каждый объект Session регистрирует время своего создания и удаления: class Session { public: Session(); -Session О; private: static void logCreat ion (Session *ob j Addr) ; static void logDestruction (Session *objAddr) ; Функции logCreation и logDestruction используются для регистрации создания и удаления объекта соответственно. Можно предположить, что деструктор Session будет иметь вид: Session::-Session О { logDestruction(this); Посмотрите теперь, что произойдет, если функция logDestruction сгенерирует исключение. Оно не будет перехвачено в деструкторе Session, а передастся модулю, вызвавшему деструктор. Но если сам деструктор был вызван в результате обработки другого исключения, то функция terminate вызывается автоматически, и ваша программа окажется полностью разрушенной. Не думаю, чтобы вы этого хотели. Конечно, невозможность запротоколировать уничтожение объекта Session - серьезный недостаток, и иногда он вызывает значительные неудобства, но ничего особенно ужасного в остановке программы нет. Поэтому достаточно принять меры, чтобы исключение, возникшее в logDe-S t rue t i on, не покинуло тело деструктора S е s s i on. Этого можно добиться только с помощью блоков try и catch. Первый вариант будет иметь, например, такой вид: Session::-Session() { try { logDestruction(this); catch (...) { cerr<< Немогу запротоколировать уничтожение объекта Session, расположенного по адресу this .\П ; Однако вряд ли этот код безопаснее предыдущего варианта. Если какой-либо вызов оператора << в блоке catch приведет к генерации исключения, оно опять покинет деструктор, и вы снова окажетесь там, откуда начали. Вы легко можете разместить блок try внутри блока catch, но это решение кажется уж слишком хитроумным. Л5ше не протоколировать удаление деструктора Session, если функция logDestruction генерирует исключение: Session::~Session() { try { logDestruction(this) ; catch (...){} He заметно, чтобы блок catch выполнял какие-либо действия, но видимость часто обманчива. Данный блок не позволяет исключениям, сгенерированным в теле logDestruction, выходить за пределы деструктора Session, а именно это и требуется. Теперь есть уверенность, что если объект Session удаляется в процессе выравнивания стека, функция terminate вызвана не будет. Существует еще одна причина, по которой не стоит допускать выход исключений за пределы деструктора. Если исключение возникает в теле деструктора и не перехвачено там же, то его выполнение останется незавершенным. (Выполнение остановится в точке генерации исключения.) Незавершенный деструктор не выполнит своих функций до конца. Взгляните, например, на измененную версию класса Session, в которой начало сессии запускает транзакцию в базе данных, а завершение сессии эту транзакцию закрывает: Session::Session{) { logCreation(this); startTransaction{) Session::~Session{) { logDestruction(this) endTransaction(); / / Для упрощения этот конструктор не обрабатывает исключения. Начнемтранзакцию в базе данных. Завершимтранзакцию / / в базе данных. В этом варианте, если функция logDestruction генерирует исключение, то транзакция, открытая в конструкторе Session, никогда не будет завершена. Возможный вариант действий - попытаться изменить порядок вызова функций в деструкторе Session, но если исключение способно возникнуть и внутри endTransaction, то обязательно надо использовать блоки try и catch. Итак, есть две серьезные причины ие допускать распространение исключений за пределы деструкторов. Во-первых, это предотвращает вызов функции terminate во время процедуры выравнивания стека. Во-вторых, гарантирует, что деструкторы полностью выполнили свои функции. Каждый из названных аргл-ментов убедителен сам по себе, но вместе они не оставляют и тени со.миенип. Правило 12. Отличайте генерацию исключения от передачи параметра или вызова виртуальной функции Объявление списка параметров функции синтаксически очень сходно с аргументом блока catch: classWidget(...}; Некий класс;неважно, что он делает. / / Все эти функции имеют аргументы / / типов Widget, VJidgeta- или Widget*. void f 1 (Widget w) ; voidf2(Widget&w) ; voidfS (constWidget&w) ; void f 4 (Widget *pw) ; void f 5 (const Widget *pw) , catch (Widget w) . . . catch(Widget&w) . . . catch(constWidget&w) . . . catch (Widget *pw) . . . catch (const Widget *pw) .. / / Bee эти catch-блокк перехватывают исключения типо1 Widget, Widget&или Widget * .
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |