Страницы

Поиск по вопросам

пятница, 29 марта 2019 г.

Как перехватить исключение

Почему данный код не ловит исключение?
#include
class A { private:
public: A() { std::cout << "A::A()" << std::endl; } ~A() { throw 1; std::cout << "A::~A(int)" << std::endl; } };
int main() { A* p = nullptr;
try { p = new A(); delete p; } catch(...) { std::cout << "catch(...)" << std::endl; }
return 0; }


Ответ

Выбрасывать исключение из деструктора не рекомендуется и на это есть ряд причин.
это может привести к утечкам памяти, невозможно обеспечить какие-либо гарантии безопасности, исключение из деструктора может вылететь во время раскрутки стека, что приведет к разным последствия в зависимости от объекта.
Посмотрим на Ваш код.
delete p;
Как известно, сначала delete-expression вызывает деструктор и затем освобождает память. Если деструктор выбрасывает исключение, то до освобождения памяти дело уже не дойдет и это приведет к утечке.
При реализации, например, контейнеров, невозможно обеспечить какие-либо гарантии относительно безопасности исключений. Например:
vector.push_back();//Вектор был не пуст
Допустим, произошло выделение памяти. Значит старые элементы копируются в новую область памяти. Если копирование прошло успешно, а при уничтожении старых объектов вылетело исключение, то мы получим часть неуничтоженных объектов и утечку памяти. Если же при копировании вылетело исключение и вектор попытается уничтожить уже сконструированные объекты, а при их уничтожении вылетит исключение, то получим ту же утечку, плюс дальше "полетит" уже другое исключение.
Если исключение вылетает из деструктора автоматического объекта во время раскрутки стека, то, в соответствии со стандартом, это приводит к вызову std::terminate
15.2 Constructors and destructors The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]
Это цитата из C++03, но в последующих стандартах поведение не поменялось.
Начиная с C++11 все деструкторы по-умолчанию имеют спецификацию исключений noexcept. При нарушении этой спецификации вызывается std::terminate:
Whenever an exception is thrown and the search for a handler (15.3) encounters the outermost block of a function with an exception-specification that does not allow the exception, then, — if the exception-specification is a dynamic-exception-specification, the function std::unexpected() is called (15.5.2), — otherwise, the function std::terminate() is called (15.5.1).
То есть, начиная с C++11 любое исключение, покинувшее деструктор, приводит к вызову std::terminate и аварийному завершению программы, тем самым, как минимум, обеспечивая единое поведение и возможность предоставления каких-либо гарантий. Деструктору возможно указать другую спецификацию исключений:
~A() noexcept(false) { /*...*/ }
Но при этом, если вылетит исключение, мы получим все проблемы указанные выше.
Итого: исключение, покинувшее деструктор создает много проблем, и не дает никакой пользы, поэтому общая рекомендация - никогда не позволять исключениям покидать деструктор.

Комментариев нет:

Отправить комментарий