Почему данный код не ловит исключение?
#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) { /*...*/ }
Но при этом, если вылетит исключение, мы получим все проблемы указанные выше.
Итого: исключение, покинувшее деструктор создает много проблем, и не дает никакой пользы, поэтому общая рекомендация - никогда не позволять исключениям покидать деструктор.
Комментариев нет:
Отправить комментарий