|
Программирование >> Обобщенные обратные вызовы
при разумном использовании деструкторов для автоматического освобождения распределенных ресурсов. 3. Когда следует использовать try и catch? Когда их не следует использовать? Изложите ваш ответ в виде рекомендации стандарта кодирования. Вот один из вариантов ответа на этот вопрос. /. Определите общую стратегию обработки ошибок и сообщений о них на уровне приложения или подсистемы и строго следуйте ей. В частности, стратегия должна охватывать, как минимум, следующие основные аспекты (а в действительности включать и многие другие вопросы). Сообщения об ошибках. Определите, о каких именно ошибках будет сообщаться и каким образом. Среди всех прочих методов сообщения об ошибках следует отдавать предпочтение исключениям. Вообще говоря, для каждой ситуации следует подобрать наиболее подходящий, удобный и легко сопровождаемый метод. Так, исключения наиболее подходят для конструкторов и операторов, которые не в состоянии вернуть значение, указывающее на происшедшую ошибку, или когда место ошибки и ее обработчик оказываются далеко друг от друга. Распространение ошибок. Помимо прочего, следует определить границы, которые не должно пересекать сгенерированное исключение. Обычно в роли таких границ выступает модуль или границы API. Обработка ошибок. Там, где это возможно, предоставьте возможность освобождения распределенных ресурсов посредством деструкторов владеющих ресурсами объектов вместо того, чтобы использовать механизм try и catch. 2. Генерируйте исключение в месте обнаружения ошибки и не пытайтесь обработать его самостоятельно. (Понятно, что если код в состоянии сам справиться с возникшей ошибкой, то он не должен сообщать о ней!) Документируйте каждую операцию - какие исключения может сгенерировать операция и почему. Такое описание должно быть частью документации каждой функции и каждого модуля. От вас не требуется писать спецификацию исключений для каждой функции (более того, как вы увидите в задаче 13, вы и не должны это делать), по вы должны ясно и точно документировать, чего следует ожидать вызывающей функции, поскольку семантика ошибок является частью интерфейса функции или модуля. 3. Используйте ключевые слова try и catch там, где у вас есть достаточно информации для обработки ошибки, ее преобразования или обеспечения границы, определенной стратегией обработки ошибок. В частности, я обнаружил, что для использования try и catch имеются три основные причины. Для обработки ошибки. Это самый простой случай; произошла ошибка, мы знаем, что делать в такой ситуации, и мы выполняем эти действия. Жизнь продолжается (уже без исходного исключения, которое приказало долго жить). Если можно, то все необходимые действия лучше выполнять в деструкторе; если нет - использовать try/catch. Для преобразования исключения. Это означает перехват одного исключения, которое сообщает о низкоуровневой ошибке, и генерацию другого исключения, в контексте своей собственной высокоуровневой семантики. Кроме того, исходное исключение может быть преобразовано в другое представление, например, код ошибки. Рассмотрим, например, класс, представляющий коммуникационное соединение, работающее с различными типами узлов и протоколов. Попытка установить соединение между двумя узлами может окончиться неуспешно из-за множества причин - например, из-за физического повреждения сети или ошибки аутентификации. Функция Open может самостоятельно обработать все эти ситуации и не сообщать о них вызывающей функции, которой не известно, что такое пакет Foo или протокол ваг. Коммуникационный класс самостоятельно обрабатывает низкоуровневые ошибки, оставаясь в согласованном состоянии, и сообщает вызывающей функции только об ошибке высокого уровня, т.е. о том, что соединение не может быть установлено. void session::open( /* ... */ ) { try { все необходимые действия catchC const ip error& err ) { - обработка lP-ошибки - освобождение ресурсов throw session::openFailed(); catchC const KerberosAuthentFai1& err ) { - обработка ошибки аутентификации - освобождение ресурсов throw Session::OpenFailed(); - - и т.д. ... Для перехвата исключений на границе подсистемы. Эта ситуация, как правило, включает преобразование исключения, обычно в код ошибки или другое представление, не основанное на механизме исключений. Например, свертка стека доходит до функции, которая является частью интерфейса вашей подсистемы для языка С, у вас есть только два варианта действий - либо немедленно вернуть код ошибки из текущей функции API, либо установить состояние ошибки, которое вызывающая функция может опросить позже при помощи дополнительной функции API GetLastError. > Рекомендация Определите общую стратегию сообщений об ошибках и их обработки в вашем приложении или подсистеме, и строго придерживайтесь се. Глобальная стратегия должна включать стратегии сообщений об ошибках, распространения ошибок и их обработки. Используйте оператор throw там, где обнаружена ошибка, но нет возможности обработать ее самостоятельно. Используйте ключевые слова try и catch там, где вы обладаете достаточной информацией для обработки ошибки, ее преобразования или обеспечения границы, определенной стратегией обработки ошибок (например, catch(...) на границе подсистемы). Резюме Один мудрый человек сказал: Либо веди сам, либо следуй за ведущим, либо уходи с дороги! Относительно безопасности исключений можно перефразировать это так: Либо генерируй исключение, либо обрабатывай его, либо уходи с дороги! На практике случай ухода с дороги зачастую оказывается основным при анализе безопасности исключений. Это и есть главная причина того, что безопасное с точки зрения исключений кодирование не сводится к расстановке try и catch в нужных местах. Задача скорее в том, чтобы суметь уйти с дороги в нужном месте. Задача 12. Безопасность исключений: стоит ли овчинка выделки? Сложность: 7 Стоит ли прилагать такие усилия по написанию безопасного с точки зрения исключений кода ? Этот вопрос - казалось бы, давно получивший однозначный ответ - все еще иногда становится предметом обсуждения. Вопрос ДЛЯ профессионала 1. Вкратце опишите гарантии безопасности исключений Абрамса (базовую, строгую, отсутствия исключений). 2. В каких случаях следует разрабатывать код, отвечающий требованиям: а) базовой гарантии? б) строгой гарантии? в) гарантии отсугствия исключений? Решение Гарантии Абрамса 1. Вкратце опишите гарантии безопасности исключений Абрамса (базовую, строгую, отсутствия исключений). Базовая гарантия (basic guarantee) заключается в том, что сбой при выполнении операции может изменить состояние программы, но не вызывает утечек и оставляет все объекты пригодными к дальнейшему использованию, в согласованном (но не обязательно предсказуемо.м) состоянии. Строгая гарантия (strong guarantee) обеспечивает семантику транзакций: при сбое операции гарантируется неизменность состояния программы, относящегося к задействованным в операции объектам. Это означает отсутствие побочных эффектов операции, влияющих на состояние объектов, включая корректность или содержимое задействованных вспомогательных объектов, таких как итераторы, указывающие внутрь контейнеров, с которыми выполнялась операция. И наконец, гарантия бессбойности (nofaiJ guarantee) означает невозможность возникновения сбоя, в контексте исключений это означает, что операция гарантированно их не генерирует. (Абраме и другие (в том числе в предыдущих книгах Exceptional С++) называли эту гарантию гарантией отсутствия исключений (nothrow guarantee). Я изменил это название на гарантию бессбойности, поскольку рассматриваемые гарантии относятся ко всем механизмам обработки ошибок, как с использованием исключений, так и без них.) Какая именно гарантия нужна 2. В каких случаях следует разрабатывать кол, отвечающий требованиям: а) базовой гарантии? б) строгой гарантии? в) гарантии отсутствия исключений? Всегда следует писать код, отвечающий по крайней мере одной из перечисленных гарантий. Тому есть несколько причин.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |