Обработка исключений
В процессе работы программы могут случаться различные непредвиденные проблемы.
Например:
- При скачивании файла оборвалось соединение...
-- Такое вполне может произойти, например если у пользователя плохой сигнал wi-fi.
- При попытке записать файл на USB устройство и устройство оказалось недоступно...
-- Такое может случиться, если пользователь задел рукой USB устройство.
- При попытке выполнить запрос к базе данных, может оказаться, что она недоступна.
-- Такое может случиться, если сервер с БД выключился/перезагрузился и т.п.
Примеров можно приводить много. Любая из этих проблем, потенциально может вывести из строя вашу программу.
Ошибки в работе программы - это конечно плохо, и к сожалению они случаются. Было бы неплохо их максимально предсказывать и если они случаются, то лучше постараться быть вкурсе и держать ситуацию под контролем.
Хорошие новости
В c++ есть механизм обработки ошибок исключений!
Исключения в C++ предоставляют механизм для обработки ошибок и нештатных ситуаций, который отделяет код обработки ошибок от основного потока выполнения программы.
Основные компоненты обработки исключений
-
try - блок кода, в котором могут возникнуть исключения
-
catch - блоки обработки исключений
-
throw - оператор генерации исключения
Базовый пример обработки исключений
-
Функция
divide()
при попытке деления на 0 генерирует исключение типаstd::runtime_error
-
Блок
try
содержит код, который может генерировать исключения -
Блок
catch
ловит конкретное исключение и обрабатывает его -
catch (...)
ловит любые исключения, не пойманные предыдущими обработчиками -
Метод
what()
возвращает сообщение об ошибке
Иерархия стандартных исключений
std::exception ├── std::logic_error │ ├── std::invalid_argument │ ├── std::domain_error │ ├── std::length_error │ └── std::out_of_range ├── std::runtime_error │ ├── std::range_error │ ├── std::overflow_error │ └── std::underflow_error └── std::bad_alloc (для ошибок выделения памяти)
Пример использования разных типов исключений:
-
Разные типы исключений используются для разных категорий ошибок
-
Обработчики расположены от наиболее специфичных к наиболее общим
-
Все стандартные исключения наследуются от
std::exception
Распространение исключений по стеку вызовов
Исключения могут распространяться вверх по стеку вызовов:-
Исключение из
innerFunction()
проходит черезmiddleFunction()
-
outerFunction()
ловит исключение, добавляет информацию и передает дальше -
main()
получает окончательно обработанное исключение
Гарантии безопасности исключений
C++ определяет три уровня гарантий безопасности исключений:
-
No-throw guarantee - функция никогда не генерирует исключений
-
Strong exception safety - при возникновении исключения состояние программы остается таким, как было до вызова функции
-
Basic exception safety - при возникновении исключения сохраняется валидность состояния, но возможна потеря данных
-
noexcept
указывает, что функция не генерирует исключений -
Реализация с сильной гарантией сначала работает с копией
-
Базовая гарантия означает, что объект останется в валидном состоянии
Исключения в конструкторах и деструкторах
-
В конструкторах исключения - основной способ сообщить об ошибке
-
Деструкторы по умолчанию
noexcept
(не должны генерировать исключения)
-
При исключении в конструкторе деструктор не вызывается
-
Ресурсы, выделенные до исключения, нужно освобождать
-
Деструкторы должны быть
noexcept
для корректной работы при раскрутке стека
Обработка исключений в C++ предоставляет мощный механизм для:
-
Разделения нормального потока выполнения и обработки ошибок
-
Гибкой обработки ошибок на разных уровнях программы
-
Создания надежного и поддерживаемого кода