Главная - Литература

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [63] 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

переполнен. Но вы можете изменить конфигурацию Windows так, что при заполнении журнала сервер будет прекращать работу. Это может быть полезно для систем повышенной секретности.

Устойчивость против корректности

Как нам показали примеры с видеоигрой и рентгеновской установкой, выбор подходящего метода обработки ошибки зависит от приложения, в котором эта ошибка происходит. Кроме того, обработка ошибок в общем случае может стремиться либо к большей корректности, либо к большей устойчивости кода. Разработчики привыкли применять эти термины неформально, но, строго говоря, эти термины находятся на разных концах шкалы. Корректность предполагает, что нельзя возвращать неточный результат; лучше не вернуть ничего, чем неточное значение. Устойчивость требует всегда пытаться сделать что-то, что позволит программе продолжить работу, даже если это приведет к частично неверным результатам.

Приложения, требовательные к безопасности, часто предпочитают корректность устойчивости. Лучше не вернуть никакого результата, чем неправильный результат. Радиационная машина - хороший пример применения такого принципа.

В потребительских приложениях устойчивость, напротив, предпочтительнее корректности. Какой-то результат всегда лучше, чем прекращение работы. Текстовый редактор, которым я пользуюсь, временами показывает последнюю на экране строку лишь частично. Хочу ли я, чтобы при обнаружении этой ситуации редактор завершал выполнение? Нет: когда я в следующий раз нажму Page Up или Page Down, экран обновится, и изображение исправится.

Влияние выбора метода обработки ошибок на проектирование высокого уровня

При наличии такого широкого выбора надо стараться реагировать на неправильные значения параметров одинаково во всей программе. Способ обработки ошибок влияет на соответствие ПО требованиям корректности, устойчивости и другим атрибутам, не относящимся к функциональности. Выбор общего подхода к работе с некорректными данными - это вопрос архитектуры или высокоуровневого проектирования, и он должен быть рассмотрен на одном из этих этапов разработки системы.

Выбрав подход, придерживайтесь его неукоснительно. Если вы решили обрабатывать ошибки на высоком уровне, а в низкоуровневом коде просто сообщать о них, удостоверьтесь, что высокоуровневый код действительно их обрабатывает! Некоторые языки позволяют игнорировать возвращаемое функцией значение (в С++ вы не обязаны что-то то делать с возвращенным результатом), но не игнорируйте информацию об ошибке! Проверяйте значение, возвращаемое из функции. Даже если вы считаете, что ошибка в функции возникнуть не может, все равно проверяйте. Весь смысл защитного программирования в защите от ошибок, которых вы не ожидаете.

Эти принципы относятся к системным функциям, так же как и вашим собственным. Если только архитектура ПО не предусматривает игнорирования сбоев системных вызовов, проверяйте коды ошибок после каждого такого вызова. При обнаружении ошибки укажите ее номер и описание.



8.4. Исключения

Исключения - это специальное средство, позволяющее передать в вызывающий код возникшие ошибки или исключительные ситуации. Если код в некотором методе встречает неожиданную ситуацию и не знает, как ее обработать, то он генерирует исключение, т. е. фактически умывает руки со словами: «Я не знаю, что с этим делать, надеюсь, кто-нибудь другой знает, как на это реагировать!» Код, не имеющий понятия о контексте ошибки, может вернуть управление другой части системы, которая, возможно, лучше знает, как интерпретировать ошибку и сделать с ней что-то осмысленное.

Кроме того, исключения могут быть полезны для упрощения запутанной логики участка кода, как в примере «Переписатьс помощью try-finally» в разделе 17.3. Вот принцип действия исключений: метод, применяя оператор throw, создает объект-исключение. Код какого-либо другого метода, стоящего выше в иерархии вызовов, перехватит это исключение в блоке try-catcb.

Популярные языки программирования по-разному реализуют исключения (табл. 8.1): Табл. 8-1. Поддержка исключений в популярных языках программирования

Параметры

обработки

исключений

Java

Visual Basic

Поддержка try-catch

Поддержка try-catch-finally

Что генерируется

Эффект при

не перехваченном

исключении

Да. Нет

Объект класса Exception или производного от него, указатель на объект, объектная ссылка, другие типы данных, например, строка или целое число.

Вызывается функция std::unexpected(), которая по умолчанию вызывает std: .terminate О, в свою очередь по умолчанию вызывающая функцию abort().

Да. Да.

Объект класса Exception или производного от него.

Да. Да.

Объект класса Exception или производного от него.

Если это «проверяемое исключение», то прекращается работа потока, в котором оно возникло. Если это «исключение периода выполнения», то оно игнорируется.

Программа завершает работу.

Генерируемые Нет.

исключения должны быть определены в интерфейсе класса

Перехватываемые Нет. исключения должны быть определены в интерфейсе класса

Нет.



Програшы. йшяьзующие ис Исключения и наследование имеют общее свойство: исполь-

ключенияш часть иорйЗйьной зуемые разумно, они могут уменьшить сложность. Исполь-

работы алгоритма, страцаш от зуемые чрезмерно, они могут сделать код абсолютно нечи-

всех проблем с читабельностью таемым. Этот раздел содержит предложения по реализации

и удобством согц)ОбОщеиш так преимуществ исключений и способы избежать трудностей, же, как й классический слагет-

которые часто с ними связаны.

$нт н Д$йв Тотс Используйте исключения для оповещения других час-(An(fy mm тй Dive Thomas) тей программы об ошибках, которые нельзя игнорировать Основное преимущество исключений состоит в их способности сигнализировать об ошибке так, что ее нельзя проигнорировать (Meyers, 1996). При других подходах к обработке ошибок есть вероятность, что сбойная ситуация останется незамеченной. Исключения устраняют такую возможность.

Генерируйте исключения только для действительно исключительных ситуаций Применение исключений должно быть зарезервировано только для действительно исключительных случаев - иначе говоря, для ситуаций, которые нельзя реализовать другими методами кодирования. Исключения используются в таких же обстоятельствах, как и утверждения: для событий, которые не просто редко происходят, а которые никогда не должны случаться.

Исключения представляют собой компромисс между возможностью обработки непредвиденных ситуаций, с одной стороны, и повышением сложности - с другой. Исключения ухудшают инкапсуляцию, требуя от кода, вызывающего метод, знать, какие исключения могут быть сгенерированы внутри него. Это усложняет код, что противоречит Главному Техническому Императиву ПО (см. главу 5), суть которого в снижении сложности.

Не используйте исключения по мелочам Если ошибка может быть обработана локально, там ее и обрабатывайте. Не генерируйте в коде неперехватывае-мое исключение, если ошибка может быть исправлена на месте.

Избегайте генерировать исключения в конструкторах и деструкторах, если только вы не перехватываете их позднее Правила обработки исключений очень быстро усложняются, когда исключения генерируются в конструкторах и деструкторах. Так, в С++ деструктор не вызывается, пока объект не создан полностью. Это значит, что, если код в конструкторе сгенерировал исключение, деструктор вызван не будет, что приведет к возможной утечке ресурсов (Meyers, 1996; Stroustrup, 1997). Аналогичные замысловатые правила относятся и к исключениям внутри деструкторов.

Приверженцы языка могут сказать, что запомнить эти правила очень легко. Но все же программисты - простые смертные, у них могут возникнуть трудности с запоминанием правил. Наилучшая программистская практика - избегать излишней сложности, которая создается при написании такого кода.

Генерируйте исключения на правильном уровне аб-

штш целостных абстракций стракции Интерфейс метода и класса должен представ-ингерфейеа ш. подазйея «Хо- лять собой целостную абстракцию. Генерируемые исключе-р01Ш абетракция» щщш 6.2. ния - такая же часть интерфейса, как и специальные типы

данных.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [63] 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294



0.0424