Reputation: 2060
I did an experiment with throwing an exception from a destructor and got a result that I did not expect. See the code snippet:
#include <iostream>
#include <exception>
class A
{
public:
~A() noexcept(false)
{
std::cout << "in ~A" << std::endl;
//throw std::runtime_error("~A exception");
}
};
class M
{
public:
~M() noexcept(false)
{
std::cout << "in ~M" << std::endl;
//throw std::runtime_error("~M exception");
}
};
class B : public A
{
public:
~B() noexcept(false)
{
std::cout << "in ~B" << std::endl;
throw std::runtime_error("~B exception");
}
private:
M m;
};
class X
{
public:
~X() noexcept(false)
{
std::cout << "in ~X" << std::endl;
//throw std::runtime_error("~X exception");
}
};
int main()
{
try
{
X x;
B b;
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
return 0;
}
the output:
in ~B
in ~M
in ~A
in ~X
~B exception
0
so the question is: is this correct that if I throw an exception from a destructor it is caught then all the subsequent destructors (including the destructors of the base class, class members and the destructors of the objects declared on the stack (as X, for example)) are called and then the exception is rethrown?
EDIT1:
The same result if I modify main()
as follows:
int main()
{
try
{
X x;
B * b = new B();
delete b;
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
return 0;
}
Upvotes: 2
Views: 80
Reputation: 118330
and then the exception is rethrown?
No, it is not "rethrown". The destructors are being called as part of throwing (and catching) the exception. This is slightly a semantical difference, but an exception gets thrown only once, here, and the objects get destroyed as part of the exception getting thrown.
Let's set aside the issue of throwing exceptions from destructors (which is, in itself, an iffy topic). Consider the following code:
void function()
{
X some_object;
throw std::runtime_error("bad");
}
X
's destructor gets called here as part of throwing the exception. Why? Because the object is getting destroyed. The destructor gets called no differently from what it gets called if this function return
s normally. The only difference is that instead of returning to the caller, the execution returns to wherever the exception gets caught (presuming that it gets caught). But, either way, execution leaves this scope, which means that the automatically-scoped object must get destroyed, which means that its destructor must get called.
A destructor of an automatically-scoped object gets called whenever execution leaves the object's automatic scope. Whether the scope is left voluntarily, or involuntarily due to a thrown exception, makes no difference.
In your case, this is no different. In fact, your code already entered the most derived-object's destructor as part of destroying it normally (via delete
or by leaving the automatic object's scope). The fact that an exception gets thrown does not really change, much, the fact that the objects get destroyed. The only difference is that the object get destroyed as part of throwing an exception, since the execution moves where it gets caught, but whether the execution moves to where it gets returned or, or where its exception gets caught, makes no difference with regards to the destructor invocation.
Upvotes: 2