Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 96
Текст из файла (страница 96)
14.6.3.2. Восстановление типа исключения Отображение неожиданного исключения в Уипехрессей позволит пользователю иелгогйей я() узнать только то, что некоторое неожиданное исключение отображено в Уииехрес!ей. Пользователь не будет знать, какое именно неожиданное исключение отображено таким образом. Эта информация теряется в функции !1ггоиУО. Простой метод позволяет сохранить и передать эту информацию.
Например, можно следующим образом собрать информацию об исключениях )телгог1с ехсер!1ои: с!авв Уипехресгей: риЫ1с Уегг риЫгс: )Уеигогй ехсер11оп * ре; Уипехресгей(Нелепей ехсерйоп* р): ре(р?р-лс(опе (): 0) ( ) -Уипехресгей() (йе!еге р; ) 462 Глава 14. Обработка исключений гоЫ И<го<ау() И<го<в(уипехрее<еИ) иу И<го<в< еа<с(< (<(еенюИ< ахсерйопа р) ( Иное Типа<<рве<ел(ар) 1 //генерация отображенного исключения ) са<с1< (... ) ( И<гон уипехрес<еИ (0) 1 ) // повторно сгенерировать и тут же перехватить) Повторная генерация и перехват исключения позволяют нам управлять любым исключением, имя которого нам известно.
Функция 11<го)гУ() вызывается из ииахрес<еИ(), вызов которой концептуально порождается из обработчика сагой (... ), так что некоторое исключение в самом деле существует и его можно генерировать повторно оператором 11<гав. При этом функция ииехрес<еИ() проигнорировать исключение и просто вернуть управление не может — попытайся она это сделать, в ней самой же будет сгенерировано исключение мИ: <йаИ ехсерпои ($14.6.3). Функция с1опе () используется для размещения копии исключения в свободной памяти. Эта копия будет су(цествовать и после того, как произойдет очистка локальных переменных обработчика исключений. 14.7. Неперехваченные исключения <уреле~чо!И(*<егт<па<е 1<пой!ег) (); <егтта<е 1<оп<дог ее« егт1па<е((егтта<е Йатйег) < Возвращаемым значением является указатель на прежнюю функцию, переданную для регистрации вет <егиииа<е () .
Функция <егт1иа<е ( ) нужна там, где нужно сменить механизм обработки исключений на что-нибудь менее тонкое. Например, функция <егиииа<е() могла бы использоваться для прекращения процесса и, возможно, для реннициализации систе- Если исключение генерируется, но не перехватывается, то вызывается функция з<И:: <егт1иа<е() . Эта функция вызывается также в случаях, когда механизм обработки исключений сталкивается с повреждением стека, или когда деструктор, вызванный в процессе раскрутки стека в связи с исключением, пытается завершить работу генерацией исключения. Как мы знаем, реакция на неожиданное исключение определяется указателем ипехрес<еИ йапИ!ег, регистрируемым функцией в<И: <ае< ииехрес<еИ() .
Аналогично, реакция на неперехваченное исключение определяется указателем иисаияй< 1<аии1ег, регистрируемым функцией з<и: < ве< <егт1иа<е() из файла кехсер11оиха 14.7. Неперекваченные исключения 463 мы. Функция гегпипаге () предназначена для принятия решительных мер в тот момент, когда стратегия восстановления системы с помощью механизма обработки исключений потерпела неудачу и пришло время для перехода на другой уровень борьбы с ошибками.
По умолчанию геглнпаге() вызывает функцию аЬогг() (59.4.1.1), что подходит в большинстве случаев, особенно во время отладки. Предполагается, что обработчик, адресуемый указателем илсаад))! лаеИег, не возвращает управление вызвавшему его коду. Если он попытается это сделать, гегт!лаге() вызовет аооггО. Отметим, что вызов а(тгт() означает ненормальный выход из программы.
Для выхода из программы можно также использовать функцию ех!г(), имеющую возврат, который указывает системе, был ли выход нормальным или ненормальным ($9.4.1.1). Вызываются ли деструкторы при завершении работы программы из-за неперехваченных исключений или нет, зависит от конкретных реализаций.
Для некоторых систем важно, чтобы деструкторы не вызывались, так как в этом случае возможно возобновление работы программы в среде отладчика. Для других систем почти невозможно не вызывать деструкторы в процессе поиска обработчика. Если вы хотите гарантировать корректную очистку для неперехватываемых исключений, нужно добавить обработчик сагс7г (... ) (514.3.2) к телу функции пга!и () помимо иных обработчиков исключений, которые вы намерены обрабатывать. Например: (и! та(п О пу ( г7...
) со!со (ага:: галде еггог) ( сегг« "галде елог: Жо! адтп! ~л"; ) сагой (ага:: оаа адос) ( сегг« "птг гап ои! о! тетогу'~п" г ) сагсп (... ) 77.. ' ) Эта конструкция перехватит любое исключение кроме тех, которые генерируются при конструировании или уничтожении глобальных объектов. Невозможно перехватить исключение, генерируемое в процессе инициализации глобального объекта. Единственный способ получить контроль при генерации исключений в этих случаях — использовать зе! илехресгеа'() (514.6.2).
Это еше одна причина по возможности избегать глобальных переменных. При перехвате исключения истинная точка программы, в которой оно было сгенерировано, неизвестна. Это меньшая информация о программе по сравнению Глава 14. Обработка исключений 464 с тем, что о ней может знать специальная программа-отладчик. Поэтому в некоторых средах разработки для некоторых программ может оказаться предпочтительным пе перехватывать исключения, восстановление от которых не предусматривается дизайном программы. 14.8.
Исключения и эффективность В принципе, обработку исключений можно организовать таким образом, что если исключение не сгенерировано, то нет и дополнительных накладных расходов. Кроме того, обработку исключений можно организовать таким образом, что она окажется не дороже вызова функции. Можно добиться этого без использования заметных дополнительных объемов памяти, а также соблюдая последовательность вызовов языка С и требования отладчиков. Это конечно не легко, но и альтернативы исключениям тоже не бесплатны — часто можно встретить традиционный код, половина объема которого занята обработкой ошибок. Рассмотрим простую функцнюу(), которая на первый взгляд не имеет никакого отношения к обработке исключений; гоЫ е (<и<); гоЫ<'() ( 5<у<ой 5; У ..
а(<) ' д(2) < ) Однако функциями() может генерировать исключение, ит() должна обеспечить корректное уничтожение строки з в этом случае. Если бы 4() не генерировала исключений, а использовала бы иной механизм для информирования об ошибках, то приведенная выше простая функция г() превратилась бы в нечто вроде следующего примера: Ьоо<'е (<и<) < Ьоо1<() ( е<г<нд ю '/... <г(д(~) ) (у(л(д) ) ге<игп <гие < еве ге<игл ~а<ее< еве ге<игл уа!ее; ) Обычно программисты не столь систематически обрабатывают ошибки, да это и не всегда нужно.
В то же время, когда систематичность в обработке ошибок нужна, лучше поручить всю эту «хозяйственную работу» компьютеру, то есть механизму обработки исключений. 14.9 Альтернативы обработке ошибок 465 Спецификации исключений (Э!4.6) особо полезны для генерации качественного кода. Если бы мы явно указали, что функция 4() не может генерировать исключений: »еЫ4(пи) гдгом(); качество машинного кода для функции г'() значительно возросло бы. Стоит отметить, что ни одна традиционная библиотечная функция языка С не генерирует исключений, так что их можно практически в любой программе объявлять с пустой спецификацией исключений тягой() . В конкретных реализациях лишь немногочисленные функции, такие как атехгт() и 4хогт(), могут генерировать исключения.
Зная эти факты, конкретные реализации могут порождать более эффективный и качественный машинный код. Перед тем, как приписать «С функции» пустую спецификацию исключений, подумайте, не может ли она все-таки генерировать исключения (прямо или косвенно). Например, такая функция могла быть модифицирована с применением операции пев языка С++, которая генерирует исключение Ьа»( аттис, или она использует средства стандартной библиотеки языка С-ь+, генерирующие исключения. 14.9.
Альтернативы обработке ошибок Механизм обработки исключений призван обеспечить передачу сообщений об «исключительных обстоятельствах» из одной части программы в другую. Предполагается также, что указанные части программы написаны независимо, и что та часть, которая обрабатывает исключение, может сделать что-либо осмысленное с возникшей ошибкой. Чтобы использовать обработчики эффективно, нужна целостная стратегия. То есть различные части программы должны договориться о том, как используются исключения и где обрабатываются ошибки. Механизм обработки исключений не локален по своей сути, и поэтому следование общей стратегии очень важно. Это подразумевает, что рассмотрение стратегии обработки ошибок лучше запланировать на самые ранние стадии проектирования.
Также важно, чтобы стратегия была простой (по сравнению со сложностью самой программы) и явной. Никто не будет придерживаться сложной стратегии в таких и без того сложных вопросах, как восстановление системы после возникновения в ней динамических ошибок. Перво-наперво, нужно отказаться от идеи, что все типы ошибок можно обработать с помощью единственной технологии — это привело бы к излишней сложности. Успешные, устойчивые к ошибкам системы обычно многоуровневые. Каждый уровень разбирается с теми ошибками, которые он способен переварить, а остальные ошибки оставляет вышестоящим уровням. Предназначение гегшглаге () состоит в том, чтобы работать в случаях, когда сам механизм обработки исключений испортился, или когда он задействован не полностью и оставляет некоторые исключения неперехваченными.
Аналогичным образом, илехресгеЫ() предоставляет выход в тех случаях, когда отказывает защитный механизм спецификации исключений. Не каждую функцию следует защищать «по полной программе». В большинстве систем невозможно снабдить каждую функцию достаточным числом проверок, гарантирующих либо ее успешное завершение, либо строго определенное поведение Глава 14.