|
Программирование >> Оптимизация возвращаемого значения
voidconvertUnexpected{) Функция, которую следует { / / вызвать при генерации throw; неожиданного исключения; просто } / / сгенерируйте снова текущее исключение. set unexpected(convertUnexpected); / / Установите convertUnexpected для замены неожиданного / / исключения. Поступив таким образом и включив bad exception (или соответствующий базовый класс, стандартный класс exception) во все спецификации исключений, вам больше никогда не придется беспокоиться об остановке программы в сл5ае возникновения неожиданного исключения. Любое сл5айное исключение будет заменено на bad exception, распространяемое вместо исходного исключения. Теперь вы понимаете, что спецификация исключений может вызвать множество неприятностей. Компиляторы осуществляют только частичную проверку их согласованного использования, их применение в шаблонах также проблематично, поскольку легко допустить нечаянную ошибку. По умолчанию при нарушении спецификации исключений выполнение программы останавливается. Спецификации исключений имеют еще один недостаток, который заключается в том, что они приводят к вызову функции unexpected, даже если для работы с возникшим исключением уже подготовлен вызывающий оператор более высокого уровня. В качестве примера рассмотрим следующую программу, которая взята без изменений из правила И: class Session{ Для моделирования сеансов public: работы в сети. -Session(); private: staticvoidlogDestruction(Session *objAddr) throwO ; Session::-Session() { try{ logdestruction(this); catch(...) {} Деструктор Session вызывает logDestruction, чтобы зафиксировать факт уничтожения объекта Session, но в действительности он обрабатывает любые исключения, которые могут быть сгенерированы функцией logDestruction. Тем не менее, спецификация исключений функции logDestruction утверждает, что эта функция не должна генерировать никаких исключений. Теперь предположим, что некая функция, вызванная logDestruction, генерирует какое-либо исключение, которое logDestruction не в состоянии перехватить. Конечно, вероятность такого развития событий невелика, но, как вы удостоверились, совсем несложно написать программу, ведущую к нарушению спецификации исключений. Когда непредвиденное исключение распространится на функцию logDestruction, будет вызвана функция unexpected, что по умолчанию приведет к завершению работы программы. Это корректное поведение, но разве такого результата хотел добиться автор деструктора Session? Напротив, автор приложил много усилий, чтобы обработать все возможные исключения, поэтому завершение работы программы без отработки блока catch в деструкторе Session кажется несправедливым. Если функция logDestruction не имеет спецификации исключений, то сценарий типа я готов обрабатывать исключения, дайте мне только шанс никогда не возникнет. (Предотвратить это можно, например, заменив функцию unexpected, как было описано выше.) Как видите, очень важно иметь точное представление о спецификациях исключений. Они обеспечивают прекрасное документирование различных видов исключений, генерируемых функцией. Для ситуаций, в которых нарушение спецификации исключений настолько ужасно , что влечет за собой немедленное завершение работы программы, они предлагают такое поведение по умолчанию. Наряду с этим спецификации проверяются компиляторами только частично и, соответственно, могут быть нарушены случайно. Более того, они могут блокировать обработку неожиданных исключений на более высоком уровне, даже если соответствующие обработчики знают, как это сделать. При таком положении дел спецификации исключений следует использовать разумно. Перед тем как вводить их в функции, подумайте, будет ли их поведение в программах соответствовать вашим планам. Правило 15. Оценивайте затраты на обработку исключений При обработке исключений во время выполнения профаммы немало усилий уходит на то, чтобы учитывать использование системных ресурсов. В каждый момент своего выполнения программа должна идентифицировать объекты, которые требуется уничтожить при генерации исгслючения. Объекты должны отметить каждый вход и выход из блока try, и для каждого блока try надо отслеживать соответствующие операторы catch и разновидности исключений, допускающие обработку этими операторами. Учет использования системных ресурсов обходится недешево. Так же, как и сравнения в ходе выполнения программы, необходимые для проверки спецификаций исключений на нарушения, или работа, затраченная на уничтожение соответствующих объектов и поиск корректного оператора catch при возникновении исключения. Без сомнения, обработка исключений требует серьезных затрат, и вам в любом случае придется покрыть хотя бы часть из них, даже если вы никогда не используете зарезервированные слова try, throw или catch. Начнем с обсуждения затрат, которые возникают, даже если вы никогда не производите обработку исключений. Они включают, во-первых, пространство, используемое специальными структурами данных для отслеживания полностью созданных объектов (см. правило 10), а также время, которое уходит на обновление этих структур. Конечно, такие затраты весьма невелики. Тем не менее, программы, составленные без поддержки исключений, обычно и выполняются быстрее, и занимают меньше места, чем их аналоги, скомпилированные с поддержкой исключений. Теоретически, у вас нет выбора: исключения являются неотъемлемой частью языка С++, и компиляторам так или иначе приходится их поддерживать. Вам также не приходится рассчитывать на то, что производители компиляторов будут удалять соответствующий код из программ, если вы не захотите использовать обработку исключений. Ведь типичные программы состоят из множества независимо генерируемых объектных файлов, и если один объектный файл не применяет исключения, вовсе не факт, что другие файлы также не делают этого. Более того, если при создании исполняемого файла ни один из связываемых объектных файлов не использует исключений, как быть с подключаемыми библиотеками? Если какая-либо часть программы обращается к исключениям, то и вся остальная программа должна тоже их поддерживать. В противном случае было бы невозможно обеспечить корректное поведение программы при обработке исключений. С теорией мы разобрались. На практике же большинство компиляторов, поддерживающих обработку исключений, позволяют вам самому решать, нужна ли эта возможность в создаваемом коде. Если вы уверены, что ни одна из частей вашей программы не содержит try, throw или catch и что ни одна из подключаемых библиотек также их не использует, то вы совершенно свободно можете компилировать программу без поддержки исключений. Это поможет сократить размер программы и увеличить скорость ее выполнения. В будущем, когда большинство библиотек станет применять исключения, такая стратегия потеряет свое значение. Однако на нынешней стадии развития языка С++, если вы все же решили отказаться от исключений, компиляция программ без их поддержки является наиболее рациональной. Эта стратегия также поможет оптимизировать обращения к библиотекам, которые избегают работы с исключениями, но только в случае, если есть гарантия, что исключения, генерируемые клиентской программой, никогда не распространятся на библиотеку. А обеспечить такую гарантию довольно трудно, потому что она запрещает переопределения клиентами виртуальных функций, объявленных в библиотеке, и определение клиентами функций обратного вызова.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |