|
Программирование >> Разработка устойчивых систем
разработка устойчивых систем На проверку готового кода уходит почти столько же времени, сколько на его создание. Качество кода является (или должно являться) главной целью каждого программиста. Научившись устранять проблемы еще до их появления, вы продвинетесь далеко на пути к этой цели. Кроме того, программные системы должны быть достаточно устойчивыми, чтобы разумно реагировать на возникновение непредвиденных внешних проблем. Исключения были добавлены в С++ как более совершенный механизм обработки ошибок, не загромождающий программу чрезмерными проверками. В главе 1 объясняется, как правильное применение исключений способствует повышению устойчивости кода, а также представляются архитектурные принципы построения программ, безопасных по отношению к исключениям. В главе 2 рассматриваются приемы модульного тестирования и отладки, повышающие качество кода еще до появления окончательной версии программы. В этом плане применение утверждений для выражения и проверки инвариантов программы является верным признаком опытного программиста. Также в главе 2 будет представлена простая система автоматизации модульного тестирования. Обработка исключений Одним из самых эффективных путей повышения надежности программ является усовершенствование механизма обработки ошибок. К сожалению, на практике ошибки часто игнорируются, словно программисты сговорились не обращать на них внимания. Несомненно, это отчасти связано с тем, что проверка многочисленных условий ошибок - занятие скучное и малоинтересное, которое к тому же приводит к разрастанию программ. Например, функция printf() возвращает количество успешно выведенных символов, однако практически никто не проверяет результат ее вызова. Увеличение объема кода само по себе неприятно, не говоря уже о дополнительных сложностях с чтением программ. У схемы обработки ошибок, использованной в С, имеется один серьезный недостаток - функция так тесно привязывается к коду обработки ошибок, что с ней становится неудобно работать. К числу важнейших новшеств С++ принадлежит механизм обработки исключений. О нем можно сказать следующее. Обработка ошибок гораздо проще программируется, а ее код не смешивается с обычным кодом. Сначала вы программируете нормальное течение событий, а позднее в отдельной секции пишется код для решения проблем. При многократном вызове функции обработка ошибок этой функции производится только один раз и в одном месте. Ошибки могут игнорироваться. Если функция должна отправить сообщение об ошибке вызывающей стороне, она запускает объект, представляющий эту ошибку. Если вызывающая сторона не перехватит ошибку и не обработает ее, то ошибка переходит в следующую внешнюю динамическую область видимости, и т. д. В итоге либо ошибка будет перехвачена, либо программа завершится из-за отсутствия обработчика для данного типа исключения. В этор! главе мы рассмотрим принципы обработки ошибок в языке С и выясним, почему они недостаточно хорошо работают в С и вообще не подходят для С++. Также будут рассмотрены ключевые слова С++ try, throw и catch, используемые при обработке исключений. Традиционная обработка ошибок в большинстве приводимых примеров директива assert() используется именно для той цели, для которой она предназначена: для отладки в процессе разработки. В окончательной версии отладочный код обычно отключается директивой #define N DEBUG. Ошибки времени выполнения проверяются функциями из файла require.h (функции assertO и require(), созданные в главе 9 первого тома, приводятся повторно в приложении Б). Эти функции означают примерно следующее: Имеется проблема, к которой следовало бы отнестись более внимательно, но мы не хотим отвлекаться на нее в данном примере . Для небольших программ функций из файла require.h может быть достаточно, но в более сложных продуктах имеет смысл использовать нетривиальную обработку ошибок. Если вы точно знаете, что нужно делать, и у вас имеется вся необходимая информация, обработка ошибок выполняется достаточно элементарно. Проблемы начинаются тогда, когда вы не обладаете всей информацией в текущем контексте, и вам необходимо передать информацию об ошибке в другой контекст, где эта информация существует. В языке С возможны три варианта действий. Можно вернуть информацию об ошибке из функции, а если возвращаемое значение не может использоваться подобным образом, установить глобальный флаг ошибки. В стандартном языке С для этой цели предусмотрены конструкции errno и perror(). Как уже упоминалось, программисты часто игнорируют информацию об ошибках, потому что проверять все возможные ошибки после каждого вызова функции было бы слишком утомительно и неудобно. Кроме того, может оказаться, что возврат из функции, в которой возникла исключительная ситуация, не имеет смысла. Можно использовать малоизвестную систему сигналов из стандартной библиотеки С, реализованную функциями signal() (определение реакции на событие) и raise() (инициирование события). Этот вариант тоже требует жесткого связывания основного кода с кодом обработки ошибок, так как пользователь любой библиотеки, генерирующей сигналы, должен знать ее систему сигналов и установить соответствующие обработчики. Кроме того, в больших проектах возможны конфликты номеров сигналов, используемых разными библиотеками. Можно использовать нелокальные версии команды перехода в виде функций setjmpO и longjmpO из стандартной библиотеки С. Функция setjmp() сохраняет заведомо нормальное состояние в программе, которое при возникновении проблем восстанавливается функцией longjmp(). Но и в этом случае требуется жесткая привязка места сохранения состояния к месту возникновения ошибки. Обсуждая схемы обработки ошибок в С++, необходимо учитывать еще одно важное обстоятельство: схемы С с сигналами и функциями setjmp()/longjmp() не
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |