- •Оглавление
- •Об авторе
- •Благодарности
- •Предисловие
- •Глава 1. Держим оборону
- •На пути к хорошему коду
- •Готовьтесь к худшему
- •Что такое защитное программирование?
- •Этот страшный, ужасный мир
- •Технологии защитного программирования
- •Выберите хороший стиль кодирования и пользуйтесь крепкой архитектурой
- •Пишите код без спешки
- •Не верьте никому
- •Стремитесь к ясности, а не к краткости
- •Не позволяйте никому лезть туда, где ему нечего делать
- •Включайте вывод всех предупреждений при компиляции
- •Пользуйтесь средствами статического анализа
- •Применяйте безопасные структуры данных
- •Проверяйте все возвращаемые значения
- •Аккуратно обращайтесь с памятью (и другими ценными ресурсами)
- •Инициализируйте все переменные там, где вы их объявили
- •Объявляйте переменные как можно позже
- •Пользуйтесь стандартными средствами языка
- •Пользуйтесь хорошими средствами регистрации диагностических сообщений
- •Выполняйте приведение типов с осторожностью
- •Подробности
- •Ограничения
- •Какие ограничения налагать
- •Снятие ограничений
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Да в чем проблема?
- •Знайте своих клиентов
- •Что такое хорошее представление?
- •Размещение скобок
- •Скобки в стиле K&R
- •Расширенный стиль скобок
- •Стиль Уайтсмита (с отступами)
- •Другие стили скобок
- •Единственно верный стиль
- •Внутрифирменные стили (и когда их придерживаться)
- •Установка стандарта
- •Религиозные войны?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Зачем нужны хорошие имена?
- •Каким объектам мы даем имена?
- •Игра в названия
- •Описательность
- •Техническая корректность
- •Идиоматичность
- •Тактичность
- •Технические подробности
- •Имена переменных
- •Имена функций
- •Имена типов
- •Пространства имен
- •Имена макросов
- •Имена файлов
- •Роза пахнет розой
- •Соблюдайте единообразие
- •Связывайте имя с содержимым
- •Извлекайте выгоду из выбора имени
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Самодокументируемый код
- •Техника написания самодокументируемого кода
- •Пишите простой код с хорошим форматированием
- •Выбирайте осмысленные имена
- •Разбивайте код на самостоятельные функции
- •Выбирайте содержательные имена типов
- •Применяйте именованные константы
- •Выделяйте важные фрагменты кода
- •Объединяйте взаимосвязанные данные
- •Снабжайте файлы заголовками
- •Правильно обрабатывайте ошибки
- •Пишите осмысленные комментарии
- •Практические методологии самодокументирования
- •Грамотное программирование
- •Инструментарий документирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Что есть комментарий в коде?
- •Как выглядят комментарии?
- •Сколько комментариев требуется?
- •Что помещать в комментарии?
- •Не нужно описывать код
- •Не подменяйте код
- •Как сделать комментарии полезными
- •Не отвлекаться
- •На практике
- •Замечание об эстетичности
- •Единообразие
- •Четкие блочные комментарии
- •Отступы в комментариях
- •Комментарии в конце строки
- •Помощь в чтении кода
- •Стиль должен обеспечивать легкость сопровождения
- •Границы
- •Флажки
- •Комментарии в заголовке файла
- •Работа с комментариями
- •Помощь при написании программ
- •Заметки об исправлении ошибок
- •Устаревание комментариев
- •Сопровождение и бессодержательные комментарии
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Откуда что берется
- •Механизмы сообщения об ошибках
- •Без обработки ошибок
- •Возвращаемые значения
- •Переменные, содержащие состояние ошибки
- •Исключения
- •Сигналы
- •Обнаружение ошибок
- •Обработка ошибок
- •Когда обрабатывать ошибки
- •Варианты реагирования
- •Последствия для кода
- •Подымаем скандал
- •Управление ошибками
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Что такое инструмент программирования?
- •А зачем они нужны – инструменты?
- •Электроинструменты
- •Выясните, каковы его возможности
- •Научитесь им управлять
- •Выясните, для каких задач он пригоден
- •Убедитесь, что он работает
- •Имейте четкие данные о том, как получить дополнительные сведения
- •Узнайте, как получить новые версии
- •Какой инструмент необходим?
- •Средства редактирования исходного кода
- •Средства построения кода
- •Инструменты для отладки и тестирования
- •Средства поддержки языка
- •Инструменты различного назначения
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Проверка на подлинность
- •Кто, что, когда, зачем?
- •Зачем тестировать
- •Кому тестировать
- •В чем состоит тестирование
- •Когда тестировать
- •Типы тестирования
- •Выбор контрольных примеров для блочного тестирования
- •Архитектура и тестирование
- •Руками не трогать!
- •Анатомия провала
- •Справлюсь ли я сам?
- •Система контроля ошибок
- •Обсуждение ошибок
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Реальные факты
- •Природа этого зверя
- •Взгляд с высоты птичьего полета
- •Взгляд с поверхности земли
- •Взгляд из глубины
- •Борьба с вредителями
- •Обходная дорога
- •Правильный путь
- •Охота за ошибками
- •Ошибки этапа компиляции
- •Ошибки этапа исполнения
- •Как исправлять ошибки
- •Профилактика
- •Отладчик
- •Средство проверки доступа к памяти
- •Трассировщик системных вызовов
- •Дамп памяти
- •Журналирование
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Языковые барьеры
- •Интерпретируемые языки
- •Компилируемые языки
- •Делаем слона из мухи
- •Выполнение сборки
- •Что должна уметь хорошая система сборки?
- •Простота
- •Единообразие
- •Повторяемость и надежность
- •Атомарность
- •Борьба с ошибками
- •Механика сборки
- •Выбор целей
- •Уборка
- •Зависимости
- •Автоматическая сборка
- •Конфигурация сборки
- •Рекурсивное применение make
- •Мастер на все руки
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Что такое оптимизация?
- •От чего страдает оптимальность кода?
- •Доводы против оптимизации
- •Альтернативы
- •Нужна ли оптимизация
- •Технические подробности
- •Убедитесь, что нужна оптимизация
- •Определите самую медленную часть кода
- •Тестирование кода
- •Оптимизация кода
- •После оптимизации
- •Методы оптимизации
- •Конструктивные изменения
- •Модификация кода
- •Как писать эффективный код
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Риски
- •Наши оппоненты
- •Оправдания, оправдания
- •Ощущение незащищенности
- •Опасный проект и архитектура
- •Переполнение буфера
- •Встроенные строки запросов
- •Условия гонки
- •Целочисленное переполнение
- •Дела защитные
- •Технология установки системы
- •Технология конструирования программного обеспечения
- •Технологии реализации кода
- •Технологии процедуры
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Программирование как конструкторская работа
- •Что нужно проектировать?
- •Хороший проект программного продукта
- •Простота
- •Элегантность
- •Модульность
- •Хорошие интерфейсы
- •Расширяемость
- •Избегайте дублирования
- •Переносимость
- •Идиоматичность
- •Документированность
- •Как проектировать код
- •Методы и процедуры проектирования
- •Инструменты проектирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Что такое программная архитектура?
- •План программы
- •Точки зрения
- •Где и когда этим заниматься?
- •Для чего она применяется?
- •Компоненты и соединения
- •Какими качествами должна обладать архитектура?
- •Архитектурные стили
- •Без архитектуры
- •Многоуровневая архитектура
- •Архитектура с каналами и фильтрами
- •Архитектура клиент/сервер
- •Компонентная архитектура
- •Каркасы
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Гниение программного обеспечения
- •Тревожные симптомы
- •Как развивается код?
- •Вера в невозможное
- •Как с этим бороться?
- •Как писать новый код
- •Сопровождение существующего кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Мартышкин труд
- •Нетерпеливый
- •Кодер (Code Monkey)
- •Гуру
- •Псевдогуру
- •Высокомерный гений
- •Ковбой
- •Плановик
- •Ветеран
- •Фанатик
- •Монокультурный программист
- •Лодырь
- •Руководитель поневоле
- •Идеальный программист
- •И что из этого следует?
- •Для глупцов
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 17. Вместе мы – сила
- •Команды – общий взгляд
- •Организация команды
- •Методы управления
- •Разделение ответственности
- •Организация и структура кода
- •Инструменты для групповой работы
- •Болезни, которым подвержены команды
- •Вавилонская башня
- •Диктатура
- •Демократия
- •Большой Каньон
- •Зыбучие пески
- •Лемминги
- •Личное мастерство и качества, необходимые для работы в команде
- •Общение
- •Скромность
- •Разрешение конфликтов
- •Обучение и приспособляемость
- •Знание пределов своих возможностей
- •Принципы групповой работы
- •Коллективное владение кодом
- •Нормы кодирования
- •Определите, что считать успехом
- •Установите ответственность
- •Избегайте истощения
- •Жизненный цикл команды
- •Создание команды
- •Рост команды
- •Групповая работа
- •Роспуск команды
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Управление версиями исходного кода
- •Контроль версий
- •Контроль доступа
- •Работа с хранилищем
- •Пусть растут деревья
- •Краткая история систем контроля за исходным кодом
- •Управление конфигурацией
- •Резервное копирование
- •Выпуск исходного кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Что же это такое, конкретно?
- •Типы спецификаций
- •Спецификация требований
- •Функциональная спецификация
- •Спецификация системной архитектуры
- •Спецификация интерфейса пользователя
- •Проектная спецификация
- •Спецификация тестирования
- •Что должны содержать спецификации?
- •Процесс составления спецификаций
- •Почему мы не пишем спецификации?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Когда проводить рецензирование?
- •Нужно ли рецензировать
- •Какой код рецензировать
- •Проведение рецензирования кода
- •Рецензирование на собраниях
- •Интеграционное рецензирование
- •Пересмотрите свое отношение
- •Позиция автора
- •Позиция рецензента
- •Идеальный код
- •За пределами рецензирования кода
- •Резюме
- •Контрольный список
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Выстрел в темноте
- •Почему трудно делать оценки?
- •Под давлением
- •Практические способы оценки
- •Игры с планами
- •Не отставай!
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Стили программирования
- •Структурное программирование
- •Функциональное программирование
- •Логическое программирование
- •Рецепты: как и что
- •Процессы разработки
- •Каскадная модель
- •SSADM и PRINCE
- •Создание прототипов
- •Итеративная и инкрементная разработка
- •Спиральная модель
- •Другие процессы разработки
- •Спасибо, хватит!
- •Выбор процесса
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 23. За гранью возможного
- •Программирование приложений
- •Коробочные продукты
- •Заказные приложения
- •Программирование игр
- •Системное программирование
- •Встроенное программное обеспечение
- •Программирование масштаба предприятия
- •Численное программирование
- •И что дальше?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 24. Что дальше?
- •Но что же дальше?
- •Ответы и обсуждение
- •Глава 1. Держим оборону
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Вопросы для размышления
- •Глава 17. Вместе мы – сила
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 20. Рецензия на отстрел
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Вопросы для размышления
- •Глава 23. За гранью возможного
- •Вопросы для размышления
- •Вопросы личного характера
- •Библиография
- •Алфавитный указатель
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Механизмыm |
сообщения об ошибках |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
135Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
чего не может сделать (кроме как постараться избегать в будущем). В идеале таких ошибок быть не должно.
Здесь возникает замкнутый круг: необрабатываемые аварийные си% туации приводят к ошибкам в программе. А эти ошибки могут при% водить к возникновению аварийных ситуаций в других местах ко% да. Поэтому мы считаем защитное программирование важной тех% нологией.
Исключительные обстоятельства
Пользователь действовал правильно, и программист ничего не напу% тал. Вмешалась неверная судьба, и мы столкнулись с чем%то, чего нельзя было избежать. Разорвалось сетевое соединение, кончились чернила в принтере или не осталось места на диске.
Необходима четкая стратегия действий для каждого типа ошибок в ко% де. Можно обнаружить ошибку и сообщить о ней пользователю во всплывающем окне, либо ошибку определит код промежуточного уров% ня и сообщит о ней коду клиента программным образом. В обоих случа% ях действуют одинаковые принципы: способ решения проблемы выби% рает либо человек, либо ваш код – кто%то должен отвечать за обнару% жение ошибки и принятие мер.
Обработка ошибок – дело серьезное. От нее зависит стабильность вашего кода.
Ошибки генерируются подчиненными компонентами и передаются на% верх, чтобы их обработал обратившийся к компоненте код. Есть ряд способов извещения об ошибке, которые мы рассмотрим в следующем разделе. Чтобы управлять выполнением программы, мы должны уметь:
•Генерировать ошибку, если возникают какие%то неприятности
•Обнаруживать все сообщения об ошибках
•Обрабатывать их надлежащим образом
•Передавать ошибку дальше, если не можем обработать ее сами
Работать с ошибками тяжело. Возникшая ошибка часто не связана с тем, чем вы занимались в данный момент (большинство из них отно% сятся к категории «непредвиденных обстоятельств»). Кроме того, об% рабатывать их скучно – хотелось бы сосредоточиться на том, что долж# на делать программа, а не на неприятностях, с которыми она может столкнуться. Однако без надлежащей обработки ошибок программа оказывается хрупкой – построенной на песке, а не на каменном осно% вании. При первых же порывах ветра она рухнет.
Механизмы сообщения об ошибках
Есть несколько стандартных стратегий передачи информации об ошиб% ке в клиентский код. Вы можете встретить код, в котором применяется
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
136m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 6. Людям свойственно ошибатьсяClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
любая из них, поэтому каждый из имеющихся диалектов должен быть вам понятен. Обратите внимание на особенности этих методов сообще% ния об ошибках и на преимущества тех или иных из них в отдельных ситуациях.
Для каждого механизма характерна своя мера близости ошибки. Ошибка близка (локальна) во времени, если обнаруживается вскоре после возникновения. Ошибка близка (локальна) в пространстве, ес% ли она обнаруживается вблизи или прямо на месте своего фактиче% ского проявления. Некоторые методы специально направлены на при% ближение ошибки, чтобы облегчить анализ происходящего (напри% мер, генерация кода ошибки). Другие методы стремятся увеличить ме% ру локальности ошибки, чтобы избавить обычный код от логики обработки сбоя (например, исключительные ситуации).
Выбор того или иного механизма часто осуществляется в рамках архи% тектурного решения. Архитектор может решить, что необходимо соз% дать однородную иерархию классов исключений или главный список общих кодов ошибок, чтобы унифицировать код, обрабатывающий ошибки.
Без обработки ошибок
Проще всего не обращать внимания на ошибки. Это лучшее решение, если вы хотите, чтобы ваша программа вела себя странным и непред% сказуемым образом и время от времени аварийно завершалась.
Если вы встретились с ошибкой и не знаете, что с ней делать, то просто игнорировать ее – неудачное решение. Продолжить работу функции, вероятно, нельзя, но вернуться из нее, не выполнив контракт функ% ции, значит, создать неопределенную и противоречивую ситуацию.
Не оставляйте сбойную ситуацию без внимания. Если вы не знаете, как справиться с проблемой, сигнализируйте об отказе вызвавшему коду. Не за& метайте мусор под ковер в надежде, что все как&нибудь обойдется.
Альтернативой игнорированию ошибок будет немедленное прерыва% ние работы программы при возникновении проблемы. Это проще, чем обрабатывать ошибки на всем протяжении кода, но вряд ли такое ре% шение можно считать технически грамотным!
Возвращаемые значения
Следующий по простоте механизм – возврат функцией значений, соот% ветствующих ее успешному/неудачному завершению. Возвращаемое булево значение дает простой ответ «да»/»нет». Более сложный под% ход состоит в нумерации всех возможных состояний и возврате соот% ветствующего кода. Одно из значений означает успех; остальные пред% ставляют различные случаи аварийного завершения. Коды заверше% ния могут быть общими для всего кода проекта, и тогда ваша функция
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Механизмыm |
сообщения об ошибках |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
137Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
будет возвращать какое%то подмножество общего списка значений. При этом в документации должно быть указано, какие коды можно получить при вызове вашей функции.
Такой подход хорош, если функции не возвращают данных; получе% ние же кодов ошибок наряду с данными вносит некоторую путаницу. Если функция int count() обходит связанный список и возвращает ко% личество элементов в нем, каким образом она может сообщить о повре% ждении структуры списка? Есть три способа:
•Возвращать составной тип данных (или кортеж, tuple), содержа% щий как возвращаемое значение, так и код ошибки. В популярных языках типа C это выглядит неуклюже и встречается редко.
•Передать код ошибки через параметр функции. В C++ и .NET этот параметр передается по ссылке. В C доступ к переменной следует организовать с помощью указателей. Данный способ некрасив и не% интуитивен; нет синтаксических средств, чтобы отличить возвра% щаемое значение от параметра.
•Можно также зарезервировать некоторый диапазон возвращаемых значений для кодов ошибок. Например, в функции подсчета все от% рицательные числа могут трактоваться как сигнал об ошибке; в ка% честве результата они все равно не имеют смысла. Отрицательные числа часто используются таким образом. Возвращаемым значени% ям типа указателя может быть присвоено специальное недопусти% мое значение, которым обычно является ноль (или NULL). В Java и C# можно возвращать нулевую ссылку на объект.
Этот прием не всегда работает удовлетворительно. Иногда трудно найти диапазон для значений, представляющих ошибки – все воз% вращаемые величины одинаково имеют смысл и одинаково вероят% ны. Кроме того, побочным эффектом является сокращение диапа% зона допустимых успешных значений; использование отрицатель% ных величин уменьшает количество возможных положительных значений на порядок.1
Переменные, содержащие состояние ошибки
В этом методе делается попытка решить конфликт между возвратом функцией значения и сообщением кода ошибки. Вместо того чтобы возвращать код состояния, функция устанавливает значение общей глобальной переменной кода ошибки. После вызова функции нужно посмотреть значение этой переменной и определить, был ли вызов ус% пешным.
Использование общей переменной сокращает путаницу и сигнатуру функции, а на диапазон возвращаемых значений вообще не оказывает никакого влияния. Однако когда для сообщения об ошибках выделяет%
1Если пользоваться типом unsigned int, то количество доступных значений
увеличивается вдвое за счет знакового разряда по сравнению с signed int.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
138m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 6. Людям свойственно ошибатьсяClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
ся отдельный канал, легко пропустить или сознательно игнорировать ошибку. Наличие общей глобальной переменной также создает боль% шие трудности для параллельной работы потоков.
В стандартной библиотеке C в этой технологии участвует переменная errno. Ее семантика очень деликатна: перед вызовом любых средств стандартной библиотеки нужно вручную обнулить errno. В случае ус% пеха ее значение не меняется; она устанавливается только в случае не% удачи. Это часто является источником ошибок, а вызов всех библио% течных функций оказывается обременительным. К тому же не все функции стандартной библиотеки C пользуются errno, поэтому в рабо% те с ней нет единообразия.
Функционально этот прием равносилен использованию возвращаемых значений, но если учесть его недостатки, то лучше им не пользоваться. Не пишите собственные сообщения об ошибках в таком стиле и с край% ней осторожностью пользуйтесь уже имеющимися реализациями.
Исключения
Исключения (исключительные ситуации) – это средство языка для об% работки ошибок; их поддерживают не все языки. Исключения помога% ют отличить нормальный ход выполнения от особых случаев, когда в функции возник сбой и она не может выполнить свой контракт. Ко% гда код сталкивается с проблемой, с которой не может справиться, он стопорит свою работу и вырабатывает исключительную ситуацию – объект, представляющий ошибку. Исполнительная система языка ав% томатически движется обратно по стеку вызовов, пока не найдет код для обработки исключительных ситуаций. Тогда программа привле% кает его, чтобы справиться с ошибкой.
Есть две модели функционирования, отличающиеся ходом событий после обработки исключительной ситуации:
Модель с прекращением исполнения
Выполнение продолжается далее после обработчика, перехватив% шего исключительную ситуацию. Такая модель существует в C++,
.NET и Java.
Модель с возобновлением
Выполнение возобновляется в том месте, где была сгенерирована исключительная ситуация.
Первую модель легче понять, но она предоставляет ограниченный конт% роль. Она позволяет только обработать ошибку (выполнить код, обна% ружив сбой), но не преодолеть ситуацию (что%то исправить и попы% таться повторить действие).
Исключительную ситуацию нельзя игнорировать. Если она не пере% хвачена и не обработана, то будет передаваться по стеку вплоть до са% мого верха и, как правило, вызовет останов программы. Исполнитель% ная система языка автоматически приводит все в порядок, разворачи%
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Механизмыm |
сообщения об ошибках |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
139Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
вая стек вызовов. Благодаря этому исключительные ситуации оказы% ваются более аккуратной и безопасной альтернативой самодельному коду обработки ошибок. Однако небрежный код генерации исключи% тельных ситуаций может вызывать утечки памяти и проблемы с очи% сткой ресурсов.1 Нужно следить за тем, чтобы код корректно рабо# тал с исключительными ситуациями. Подробнее о том, что это озна% чает, рассказывается во врезке.
Код, который обрабатывает исключительную ситуацию, и тот, кото% рый генерирует ее, различны и могут находиться совсем в разных мес% тах. Механизм исключительных ситуаций обычно предоставляется ОО%языками, и ошибки в них определяются как иерархия классов ис% ключительных ситуаций. Обработчик может быть предназначен для перехвата вполне конкретного класса ошибок (принимая производ% ный класс) или более общей категории ошибок (принимая базовый класс). Исключительные состояния представляют особую ценность для сигнализации об ошибках в конструкторах.
Поддержка исключительных ситуаций не проходит даром – она отра% жается на производительности программ. На практике потери несуще% ственны и проявляются только в операторах, занимающихся обработ% кой исключений, – обработчики исключительных ситуаций мешают компилятору выполнить оптимизацию. Из этого не следует, что меха% низм исключительных ситуаций порочен; его издержки вполне оправ% даны по сравнению с последствиями отсутствия обработки ошибок!
Агитпоход за безопасность исключений
Прочный код должен хорошо «держать» исключительные си# туации. Он должен работать корректно (какой смысл в это вкла% дывается, обсуждается ниже), независимо от того, какие возника% ют исключительные ситуации. Это не должно зависеть от того, перехватывает ли сам код какие%либо исключительные ситуации.
Нейтральный в отношении исключительных ситуаций код пе% ресылает все исключительные ситуации наверх, тому, кто его вызвал; сам он ничего не трогает и не меняет. Это важная кон% цепция для таких общих программ, как код шаблонов C++; типы шаблонов могут генерировать самые различные исключитель% ные ситуации, не известные тем, кто разрабатывает шаблоны.
1Например, можно выделить блок памяти, а затем неожиданно выйти из функции из%за возникшей исключительной ситуации. Выделенная память оказывается потерянной. Такие проблемы осложняют написание кода, ко% торый должен работать в условиях возможности генерации исключитель%
ных ситуаций.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
140m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 6. Людям свойственно ошибатьсяClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Есть несколько уровней защиты от исключительных ситуаций. Они характеризуются теми гарантиями, которые даются вызы% вающему коду. Вот эти гарантии:
Базовые гарантии
Если в функции возникают исключительные ситуации (в ре% зультате выполняемых действий или вызова других функ% ций), они не приводят к утечке ресурсов. Код сохраняет рабо% тоспособность (т. е. им можно корректно пользоваться), но со% стояние его может оказаться неопределенным. Пример: функ% ция%член должна добавить в контейнер 10 элементов, но ее работа прервана исключительной ситуацией. Контейнер со% храняется в рабочем состоянии; при этом может оказаться, что в него не было добавлено ни одного объекта, либо добавле% ны все 10, либо только половина всех объектов.
Сильные гарантии
Они значительно строже, чем базовые. Если во время выпол% нения кода возникает исключительная ситуация, состояние программы абсолютно не меняется. Не изменится ничего – ни один объект, ни одна глобальная переменная. В предыдущем примере в контейнер не будет ничего помещено.
Гарантии невозбуждения исключительных ситуаций
Последний вид гарантий наиболее строг: операция не может создать исключительную ситуацию. Для нейтрального в от% ношении исключительных ситуаций кода это означает, что функция не должна делать ничего такого, что может генери% ровать исключительные ситуации.
Вид предоставляемых гарантий зависит исключительно от вас. Чем строже гарантии, тем шире возможности использо% вания кода. Для реализации сильных гарантий обычно требу% ется иметь несколько функций, обеспечивающих гарантию отсутствия исключительных ситуаций.
Прежде всего, каждый деструктор должен обеспечивать га% рантию невозбуждения исключений.1 В противном случае не% возможно обеспечить обработку исключений. При наличии исключительной ситуации деструкторы объектов автомати% чески вызываются во время развертывания стека. Генерация исключения во время обработки исключения недопустима.
1Так, по крайней мере, происходит в C++ и Java. В C# деструктором назван ~X(), что глупо, поскольку это лишь замаскированный фина% лизатор. Генерация исключения в деструкторе C# приводит к дру% гим последствиям.