Исключительные ситуации
Обработка ошибок и непредвиденных ситуаций В процессе работы программы возможно возникновение непредвиденных ситуаций Нехватка памяти и других системных ресурсов Ошибки ввода-вывода Некорректные данные, поступившие от пользователя Нарушение целостности данных (поврежден файл с данными) Некорректные параметры функций Хорошо спроектированная программа должна уметь обнаруживать, сигнализировать и обрабатывать данные ситуации Сообщить об ошибке пользователю Попытаться исправить ошибки При невозможности дальнейшей работы – сохранить данные и завершить работу
Обнаружение ошибочных ситуаций Проверяйте данные поступающие из внешних источников на корректность Файлы, введенные пользователем данные, сетевые пакеты и т. п. Осуществляйте проверку успешности вызовов функций используемого API Функции ввода/вывода, выделения/освобождения ресурсов
Способы обработки ошибок Проигнорировать ошибку, оставив программу в неопределенном состоянии Самых худший вариант – никогда так не делайте! Вывод сообщения об ошибке и аварийное завершение работы программы Немногим лучше предыдущего Поместить код ошибки в глобальную переменную Проблемы с многопоточными приложениями Предусмотреть специальное значение функции, сигнализирующее об ошибке А как возвращать нормальное значение? Вызвать функцию-обработчик ошибки Часто необходимо передавать контекст возникновения ошибки Необходимо восстанавливать нормальное выполнение Воспользоваться механизмом исключений языка C++
Механизм исключений Встроенное в язык C++ средство для обработки внештатных ситуаций во время выполнения программы Исключения позволяют программе обработать внештатную ситуацию на более высоком уровне, на котором возможно восстановить нормальную работу программы Обработка исключений осуществляется кодом вне обычного потока выполнения
Оператор try-catch Синтаксис try { код, в котором возможно генерирование исключений } catch(объявление исключения) { код, обрабатывающий исключение } [catch (…) { код, обрабатывающий исключения любого типа }]
Оператор throw Синтаксис: throw [выражение] Выражение может быть любого типа (кроме void) При выполнении данного оператора создается объект исключительной ситуации на основе объекта, выступающего в качестве аргумента оператора throw Дальнейшее выполнение программы прерывается, происходит поиск подходящего обработчика в защищенном блоках trycatch Если подходящий обработчик найден, то происходит раскрутка стека (stack unwinding), при которой разрушаются все автоматические объекты, созданные между началом соответствующего блока try Если обработчик не найден, происходит завершение работы программы
Классы исключений Использование классов в качестве типа объектов исключений имеет ряд преимуществ Возможность хранения подробной информации об исключении Возможность использования полиморфизма Оператор catch ловит исключения не только указанного типа, но и всех производных от него типов: try { throw CDerived. Exception; } catch (CBase. Exception const& e) { }
Выбрасывание и перехват исключений в C++ Исключения всегда выбрасываются «по значению» У классов исключений должен быть доступен конструктор копирования Перехват исключений должен происходить по ссылке В противном случае может произойти «урезание» информации об исключении
#include <stdio. h> <math. h> <string> <stdexcept> double My. Sqrt(double arg) { if (arg < 0) { throw std: : invalid_argument("the argument must be >= 0"); } return sqrt(arg); } int main() { try { std: : cout << "sqrt(3) = " << My. Sqrt(3) << "n"; std: : cout << "sqrt(-1) = " << My. Sqrt(-1) << "n"; } catch (std: : invalid_argument const & e) { std: : cout << "Error: " << e. what() << "n"; } return 0; } Output: sqrt(3) = 1. 732051 Error: argument must be >= 0
Перевыброс исключения Перехватив исключение в блоке catch можно перевыбросить его снова, чтобы оно было перехвачено внешним обработчиком Синтаксис: throw; Пример: try { . . . } catch(Some. Error { //. . . throw; } const& e)
Стандартные классы исключений библиотеки STL exception ios_base: : failure bad_typeid invalid_argument logic_error out_of_range bad_exception length_error domain_error bad_alloc runtime_error overflow_error bad_cast range_error underflow_error
Основные классы исключений STL exception – базовый класс для всех исключений, выбрасываемых кодом библиотеки logic_error – базовый класс для ошибок, которые можно было бы выявить до выполнения программы runtime_error – базовый класс ошибок, которые, как правило, можно выявить только во время работы программы bad_alloc – ошибка выделения памяти bad_cast – ошибка приведения типа
Преимущества использования исключений Реакция на исключение происходит всегда Коды ошибок работают только тогда, когда их проверяют Возможность различной реакции на различные типы исключений Объекты, выбрасываемые при исключении могут нести доп. информацию об исключительной ситуации имя файла и номер строки, сообщение об ошибке, код системной ошибки и т. п.
Проблемы При использовании исключений необходимо иметь представление о том, какие исключения могут быть выброшены в результате работы функции или метода класса Увеличение размеров машинного кода и некоторое снижение его быстродействия Необходимость разработки кода, устойчивого к возникновению исключений
Выброс исключения в конструкторе При выбросе исключения в теле конструктора или в списке инициализации процесс конструирования экземпляра класса прерывается и он считается не созданным Деструктор для такого класса вызван не будет Будут вызваны деструкторы для тех полей класса, для которых успели выполниться конструкторы Порядок вызова конструкторов полей класса совпадает с порядком их перечисления в объявлении класса Порядок вызова деструкторов – обратный порядку вызова конструкторов
class A { public: A(std: : string const& name) : m_name(name) { std: : cout << "A: : A(" << m_name << ")n"; } ~A() { std: : cout << "A: : ~A(" << m_name << ")n"; } private: std: : string m_name; }; int main() { try { B b 2("Test", 100000); } catch (std: : bad_alloc const & e) { std: : cout << "Error: " << e. what() << "n"; } return 0; } class B { public: B(std: : string const& name, size_t size) : m_a(name) , m_p. Data(new int[size]) , m_size(size) { std: : cout << "B: : B(" << m_size << ")n"; } ~B() { delete [] m_p. Data; std: : cout << "B: : ~B(" << m_size << ")n"; } private: A m_a; Нехватка памяти и size_t m_size; int * m_p. Data; выброс исключения }; std: : bad_alloc Output: A: : A(Test) A: : ~A(Test) Error: bad allocation
Выброс исключений в деструкторе Не допускайте выброса исключений в деструкторах объектов В C++ выброс исключения в деструкторе приводит к аварийному завершению работы программы
Exception-safe programming Разработка кода, безопасного к возникновению исключений
Код, устойчивый к возникновению исключений Объект, как минимум, должен оставаться destructible – последующий вызов деструктора данного объекта не должен приводить к сбою в приложении или неопределенному поведению При выбросе исключений не должно происходить утечек памяти и других ресурсов Объект, желательно, должен сохранить свою целостность, т. е, последующие вызовы методов объекта не должны приводить к сбоям или неопределенному поведению Желательно, объект должен вернуться в предсказуемое состояние, а еще лучше, в состояние, в котором он был до вызова метода, выбросившего исключение
class CMy. String { public: CMy. String(const char * str): m_size(strlen(str)) { m_p. Chars = new char[m_size + 1]; memcpy(m_p. Chars, str, m_size); m_p. Chars[m_size] = '