- 1. Инварианты
- 2. Виды исключений
- 3. std::logic_error
- 4. std::invalid_argument
- 5. std::domain_error
- 6. std::length_error
- 7. std::out_of_range
- 8. std::future_error
- 9. std::runtime_error
- 10. std::range_error
- 11. std::overflow_error
- 12. std::underflow_error
- 13. std::system_error
- 14. std::ios_base::failure
- 15. std::bad_typeid
- 16. std::bad_cast
- 17. std::bad_weak_ptr
- 18. std::bad_function_call
- 19. std::bad_alloc
- 20. std::bad_array_new_length
- 21. std::bad_exception
Что такое исключение? Это ситуация, которая не предусмотрена стандартным поведением программы. Например, попытка доступа к элементу в классе Vector (который мы разбирали в статье про классы ), который не существует. То есть происходит выход за пределы вектора. В данном случае можно воспользоваться исключениями, чтобы прервать выполнение программы. Это необходимо потому, что
- Как правило в таких случаях, автор класса Vector не знает, как пользователь захочет использовать его класс, а также не знает в какой программе этот класс будет использоваться.
- Пользователь класса Vector не может всегда контролировать правильность работы этого класса, поэтому ему нужно сообщить о том, что что-то пошло не так.
Для разрешения таких ситуация в C++ можно использовать технику исключений.
Рассмотрим, как написать вызов исключения в случае попытки доступа к элементу по индексу, который не существует в классе Vector.
- double& Vector::operator[](int i)
- {
- if (i<0 || size()<=i) throw out_of_range{"Vector::operator[]"};
- return elem[i];
- }
Здесь применяется исключение
out_of_range.
Данное исключение определено в заголовочном файле
Оператор throw передаёт контроль обработчику для исключений типа out_of_range в некоторой функции, которая прямо или косвенно вызывает Vector::operator . Для того, чтобы обработать исключения необходимо воспользоваться блоком операторов try catch.
- void f(Vector& v)
- {
- // ...
- try { // блок обработки функции с исключением
- v[v.size()] = 7; // попытка доступа к элементу за пределами вектора
- }
- catch (out_of_range) { // ловим ошибку out_of_range
- // ... обработки ошибки out_of_range ...
- }
- // ...
- }
Инварианты
Также блоки try catch позволяют производить обработку нескольких различных исключений, что вносит инвариантность в работу механизма исключений C++.
Например, класс вектор при создании может получить неправильный размер вектора или не найти свободную память для элементов, которые он будет содержать.
- Vector::Vector(int s)
- {
- if (s < 0) throw length_error{};
- elem = new double[s];
- sz = s;
- }
Данный конструктор может выбросить исключение в двух случаях:
- Если в качестве аргумента size будет передано отрицательное значение
- Если оператор new не сможет выделить память
length_error - это стандартный оператор исключений, поскольку библиотека std часто использует данные исключения при своей работе.
Обработка исключений будет выглядеть следующим образом:
- void test()
- {
- try {
- Vector v(−27);
- }
- catch (std::length_error) {
- // обработка отрицательного размера вектора
- }
- catch (std::bad_alloc) {
- // обработка ошибки выделения памяти
- }
- }
Также можно выделить свои собственные исключения.
Виды исключений
Все исключения стандартной библиотеки наследуются от std::exception.
На данный момент существуют следующие виды исключений:
- logic_error
- invalid_argument
- domain_error
- length_error
- out_of_range
- future_error (C++11)
- runtime_error
- range_error
- overflow_error
- underflow_error
- system_error (C++11)
- ios_base::failure (начиная с C++11)
- bad_typeid
- bad_cast
- bad_weak_ptr (C++11)
- bad_function_call (C++11)
- bad_alloc
- bad_array_new_length (C++11)
- bad_exception
- ios_base::failure (до C++11)
std::logic_error
Исключение определено в заголовочном файле
Определяет тип объекта, который будет брошен как исключение. Он сообщает об ошибках, которые являются следствием неправильной логики в рамках программы, такие как нарушение логической предпосылки или класс инвариантов, которые возможно предотвратить.
Этот класс используется как основа для ошибок, которые могут быть определены только во время выполнения программы.
std::invalid_argument
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в случае неправильного аргумента.
Например, на MSDN приведён пример, когда в объект класса bitset из стандартной библиотеки
- // invalid_arg.cpp
- // compile with: /EHsc /GR
- #include <bitset>
- #include <iostream>
- using namespace std;
- int main( )
- {
- try
- {
- bitset< 32 > bitset( string( "11001010101100001b100101010110000") );
- }
- catch ( exception &e )
- {
- cerr << "Caught " << e.what( ) << endl;
- cerr << "Type " << typeid( e ).name( ) << endl;
- };
- }
- \* Output:
- Caught invalid bitset<N> char
- Type class std::invalid_argument
- *\
В данном примере передаётся неправильная строка, внутри которой имеется символ 'b', который будет ошибочным.
std::domain_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в случае если математическая функция не определена для того аргумента, который ей передаётся, например:
- std::sqrt(-1)
std::length_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть броше в том случае, когда осуществляется попытка реализации превышения допустим пределов для объекта. Как это было показано для размера вектора в начале статьи.
std::out_of_range
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в том случае, когда происходит выход за пределы допустимого диапазона значений объекта. Как это было показано для диапазона значений ветора в начале статьи.
std::future_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Данное исключение может быть выброшено в том случае, если не удалось выполнить функцию, которая работает в асинхронном режиме и зависит от библиотеки потоков. Это исключение несет код ошибки совместимый с std::error_code .
std::runtime_error
Исключение определено в заголовочном файле
Является базовым исключением для исключений, которые не могут быть легко предсказаны и должны быть брошены во время выполнения программы.
std::range_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой, когда компьютер не может обработать значение, поскольку оно является либо слишком большим, либо слишком маленьким. Если значение является значение интегрального типа, то должны использоваться исключения underflow_error или overflow_error .
std::overflow_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой интегрального типа, когда число имеет слишком большое положительное значение, положительную бесконечность, при которой происходит потеря точности, т.е. результат настолько большой, что не может быть представлен числом в формате IEEE754.
std::underflow_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой интегрального типа, при которой происходит потеря точности, т.е. результат настолько мал, что не может быть представлен числом в формате IEEE754.
std::system_error
Исключение определено в заголовочном файле
std::system_error - это тип исключения, которое вызывается различными функциями стандартной библиотеки (как правило, функции, которые взаимодействуют с операционной системой, например, конструктор std::thread ), при этом исключение имеет соответствующий std::error_code .
std::ios_base::failure
Исключение определено в заголовочном файле
Отвечает за исключения, которые выбрасываются при ошибках функций ввода вывода.
std::bad_typeid
Исключение определено в заголовочном файле
Исключение этого типа возникает, когда оператор typeid применяется к нулевому указателю полиморфного типа.
- #include <iostream>
- #include <typeinfo>
- struct S { // Тип должен быть полиморфным
- virtual void f();
- };
- int main()
- {
- S* p = nullptr;
- try {
- std::cout << typeid(*p).name() << '\n';
- } catch(const std::bad_typeid& e) {
- std::cout << e.what() << '\n';
- }
- }
std::bad_cast
Исключение определено в заголовочном файле
Данное исключение возникает в том случае, когда производится попытка каста объекта в тот тип объекта, который не входит с ним отношения наследования.
- #include <iostream>
- #include <typeinfo>
- struct Foo { virtual ~Foo() {} };
- struct Bar { virtual ~Bar() {} };
- int main()
- {
- Bar b;
- try {
- Foo& f = dynamic_cast<Foo&>(b);
- } catch(const std::bad_cast& e)
- {
- std::cout << e.what() << '\n';
- }
- }
std::bad_weak_ptr
Исключение определено в заголовочном файле
std::bad_weak_ptr – тип объекта, генерируемый в качестве исключения конструкторами std::shared_ptr , которые принимают std::weak_ptr в качестве аргумента, когда std::weak_ptr ссылается на уже удаленный объект.
- #include <memory>
- #include <iostream>
- int main()
- {
- std::shared_ptr<int> p1(new int(42));
- std::weak_ptr<int> wp(p1);
- p1.reset();
- try {
- std::shared_ptr<int> p2(wp);
- } catch(const std::bad_weak_ptr& e) {
- std::cout << e.what() << '\n';
- }
- }
std::bad_function_call
Исключение определено в заголовочном файле
Данное исключение генерируется в том случае, если был вызван метод std::function::operator() объекта std::function , который не получил объекта функции, то есть ему был передан в качестве инициализатора nullptr, например, а объект функции так и не был передан.
- #include <iostream>
- #include <functional>
- int main()
- {
- std::function<int()> f = nullptr;
- try {
- f();
- } catch(const std::bad_function_call& e) {
- std::cout << e.what() << '\n';
- }
- }
std::bad_alloc
Исключение определено в заголовочном файле
Вызывается в том случае, когда не удаётся выделить память.
std::bad_array_new_length
Исключение определено в заголовочном файле
Исключение вызывается в следующих случаях:
- Массив имеет отрицательный размер
- Общий размер нового массива превысил максимальное значение, определяемое реализацией
- Количество элементов инициализации превышает предлагаемое количество инициализирующих элементов
- #include <iostream>
- #include <new>
- #include <climits>
- int main()
- {
- int negative = -1;
- int small = 1;
- int large = INT_MAX;
- try {
- new int[negative]; // negative size
- new int[small]{1,2,3}; // too many initializers
- new int[large][1000000]; // too large
- } catch(const std::bad_array_new_length &e) {
- std::cout << e.what() << '\n';
- }
- }
std::bad_exception
Исключение определено в заголовочном файле
std::bad_exception - это тип исключения в C++, которое выполняется в следующих ситуациях:
- Если нарушается динамическая спецификация исключений
- Если std::exception_ptr хранит копию пойманного исключения, и если конструктор копирования объекта исключения поймал current_exception, тогда генерируется исключение захваченных исключений.
- #include <iostream>
- #include <exception>
- #include <stdexcept>
- void my_unexp() { throw; }
- void test() throw(std::bad_exception)
- {
- throw std::runtime_error("test");
- }
- int main()
- {
- std::set_unexpected(my_unexp);
- try {
- test();
- } catch(const std::bad_exception& e)
- {
- std::cerr << "Caught " << e.what() << '\n';
- }
- }
std::underflow_error - это не "число имеет слишком большое отрицательное значение", а потеря точности при вычислениях, т.е. результат настолько мал, что не может быть представлен числом в формате IEEE754
Спасибо. Дополнил.