Почему данный код не ловит исключение?
#include <iostream>
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
Это цитата из 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) { /*...*/ }
Но при этом, если вылетит исключение, мы получим все проблемы указанные выше.
Итого: исключение, покинувшее деструктор создает много проблем, и не дает никакой пользы, поэтому общая рекомендация - никогда не позволять исключениям покидать деструктор.
Вообще-то в деструкторах генерировать исключения категорически не рекомендуется. Как я понимаю (на конкретное место в стандарте не укажу) - фактически деструкторы объявлены как noexcept - а значит, при генерации в них исключения будет вызван terminate.
Вот, выполните этот код:
#include <iostream>
#include <stdlib.h>
class A
{
private:
public:
A()
{
std::cout << "A::A()" << std::endl;
}
~A()
{
throw 1;
std::cout << "A::~A(int)" << std::endl;
}
};
int main()
{
std::set_terminate([](){ std::cout << "terminate called\n"; exit(1);});
A* p = nullptr;
try
{
p = new A();
delete p;
}
catch(...)
{
std::cout << "catch(...)" << std::endl;
}
return 0;
}
Как видите, вызывается terminate - как результат генерации исключения там, где его быть не должно.
"По-моему, так" (с) Пух
Сборка персонального компьютера от Artline: умный выбор для современных пользователей