I understand that the current rules in C++ say that:
if a destructor throws while already stack unwinding because of a exception then std::terminate is called.
While exploring why the rules are as they are, I came across the situation described in the code below.
The destructor of X throws.
Y deletes an X in its own destructor.
Therefore the destructor of Y throws.
It's not clear to me if the fact that Y throws (3.) should trigger std::terminate by the standard rules. I hope it should not, and testing against gcc runs as I hoped.
Can someone familiar with the standard legalese clarify this? Should (3.) trigger std::terminate or not?
#include <iostream>
struct X {
~X() noexcept(false) {
std::cout << "Destroying X\n";
throw std::runtime_error("Exception");
}
};
struct Y {
X * x_;
explicit Y(X * x) : x_{x} { }
~Y() noexcept(false) {
std::cout << "Destroying Y\n";
delete x_;
}
};
int main() {
try {
Y y(new X());
std::cout << "Living\n";
}
catch (const std::exception & e) {
std::cout << "Caught " << e.what() << '\n';
}
}
With g++ version 5.4.0-6ubuntu1~16.04.9 with --std=c++17 I get:
Living
Destroying Y
Destroying X
Caught Exception
The standard says in [except.terminate]p1.4:
when the destruction of an object during stack unwinding terminates by throwing an exception, or
2) happens because y goes out of scope. 1) throws an exception, which starts stack unwinding. During stack unwinding, more specifically while destroying Y - which is 2). It throws an exception, and so the point is satisfied and std::terminate is called.
Which is exactly what you're code is doing, except for an important point: during stack unwinding. Stack unwinding doesn't happen when a scope ends, it only happens when an exception is thrown and goes out of the current scope it was thrown.
Y isn't destroyed because of an exception. Add a throw 1; to see a call to std::terminate in action.
Thus, that clause doesn't apply and your code is indeed valid.
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can a local variable’s memory be accessed outside its scope?
Code:
#include <iostream>
using namespace std;
class B{
public:
int b;
B():b(1){}
~B(){cout << "Destructor ~B() " << endl;}
};
class A{
public:
B ob;
A()try{throw 4;}
catch(...){cout << "Catched in A() handler : ob.b= " << ob.b<<endl;}
};
int main()try{
A t;
}
catch(...){cout << "CATCHED in Main" << endl;}
Output:
Destructor ~B()
Catched in A() handler : ob.b= 1
CATCHED in Main
My Question is how it's possible to access a member variable b of an object ob that its destructor call finished.
Using a destructed object is undefined behaviour. This means that you may be getting this behaviour now, but nothing guarantees that you will get it some other time. Undefined behaviour is more dangerous than a regular bug because it can be more difficult to detect, as this example shows.
UPDATE: Following some comments, I'll explain why OP's code produces that output.
try function blocks are a somewhat obscure feature in C++. You can surround the whole body of a function inside a try block, with its corresponding catch. This is, instead of:
void foo()
{
try
{
//...
}
catch (/*whatever*/)
{
//...
}
}
You can write:
void foo()
try
{
//...
}
catch (/*whatever*/)
{
//...
}
This is not too useful, really, but can be marginally useful for constructors, because this is the only way to include the initialisation list inside the try block. Thus, OP's code for the A constructor is equivalent to:
A()
try
: b()
{
throw 4;
}
catch(...)
{
cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
}
I said this is just marginally useful because you cannot use the object you are constructing inside the catch block; if you throw inside the try block, the exception will leave the constructor body, so the object will have never been constructed and any constructed data member will be immediately destructed before entering the catch block. But it might have some use for logging purposes.
Now, as we all know, a constructor (asuming we are not using the nothrow version) can only do two things:
Return a constructed object
Throw an exception
In this constructor we throw, but the exception is caught in the catch block. So what happens now? What will be returned to the code calling the constructor? We cannot return a constructed object because we have none, so there is only one alternative: the catch block must throw. And this is actually what the standard mandates in this case. If we do not throw explicitly, the compiler will silently add a throw; instruction at the end of the catch block. So, elaborating a bit more, the constructor is equivalent to the following:
A()
try
: b()
{
throw 4;
}
catch(...)
{
cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
throw;
}
And this is the reason why the exception is caught twice: once in A constructor and once in main().
Because while the object has been destroyed, the actual memory that it occupied still exists. However, it's undefined behavior, and may or may not work.
That might be a bug in your compiler.
When I ran your code, the destructor is called in the proper order. The output is:
Catched in A() handler : ob.b= 1
Destructor ~B()
And I can't imagine any reason why catch in main is executed. The exception was already caught in A::A().
Update
I was confused by the edit. Originally, it was initializer list try/catch syntax which makes a difference. Now I can reproduce OP's output and it is UB indeed. I got a crash.
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;
}
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 returns 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.
Why does the following exception thrown from the constructor of class A get caught twice, first by the catch within the constructor itself and second time by the catch in the main function?
Why doesn't it get caught just once by the catch within the constructor?
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { }
};
class A {
public:
int i;
A() try : i(0) {
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
}
};
int main() {
try {
A x;
}
catch(...)
{
cout << "Exception caught" << endl;
}
}
If I remove the try-catch block in the main function, the program will crash.
Here is the output:
Exception thrown in A()
terminate called after throwing an instance of 'E'
zsh: abort (core dumped) ./main
Why does it crash without the try-catch block in the main function?
Function-try-blocks in a constructor cannot prevent exceptions. Once an exception occurs in a constructor, you have no object, and the exception must propagate. The only thing the function-try-block can do is some local clean-up.
Constructors are indeed a very special animal with regards to function-try-blocks.
Cf. C++11 15.3/14:
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Tl;dr: Do not use function-try-blocks, ever.
It seems logical. Consider two following scenarios.
i. Try block is inside constructor's body:
A() : i(0) {
try
{
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
}
// If code reaches here,
// it means the construction finished well
}
ii. Try block is in initializer ctor:
A() try : i(0) {
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
// OK, you handled the exception,
// but wait you didn't construct the object!
}
In the first case, after an exception, you will handle it inside the constructor and then you will construct the object properly.
In the second case, after an exception you will handle it there. BUT you didn't construct the object yet and you have no object in the caller's side. The caller should handle an un-constructed object situation.
You are utilizing a feature called function-try-catch. When used in a constructor, it allows catching exceptions in the initialization list (especially useful for catching exceptions in base class constructors) as well as the constructor body. But since the exception is thrown in a constructor, the class is incomplete, so the compiler automatically rethrows any caught exception.
that is why you see it caught twice.
Read the following article for more details:
Constructors and Exception in C++
This question is inspired by Using an object after it's destructor is called
Let's consider the following code
class B
{
public:
B() { cout << "Constructor B() " << endl; }
~B() { cout << "Destructor ~B() " << endl; }
};
class A {
public:
B ob;
A()
try
{
throw 4;
}
catch(...)
{
cout << "Catch in A()\n";
}
A(int)
{
try
{
throw 4;
}
catch(...)
{
cout << "Catch in A(int)\n";
}
}
};
int main()
{
try
{
A f;
}
catch (...)
{
cout << "Catch in main()\n\n";
}
A g(1);
}
It's output is
Constructor B()
Destructor ~B()
Catch in A()
Catch in main()
Constructor B()
Catch in A(int)
Destructor ~B()
In contrast to A(int), constructor A() has the initializer list try/catch syntax. Why that makes a difference on the order of the subobject destruction? Why the exception thrown in A() propagates to main()?
Why that makes a difference on the order of the subobject destruction?
When catch in A(int) - all subobjects are alive, and you can use them. Moreover, after catch, you can continue object construction, and "return" to user properly constructed object.
When catch in A() - all subobjects, which were constructed - are destructed. And you don't know which subobjects were constructed and which not - at least with current ISO C++ syntax.
Why the exception thrown in A() propagates to main()?
Check GotW #66:
If the handler body contained the statement "throw;" then the catch block would obviously rethrow whatever exception A::A() or B::B() had emitted. What's less obvious, but clearly stated in the standard, is that if the catch block does not throw (either rethrow the original exception, or throw something new), and control reaches the end of the catch block of a constructor or destructor, then the original exception is automatically rethrown.
Think about what this means: A constructor or destructor function-try-block's handler code MUST finish by emitting some exception. There's no other way. The language doesn't care what exception it is that gets emitted -- it can be the original one, or some other translated exception -- but an exception there must be! It is impossible to keep any exceptions thrown by base or member subobject constructors from causing some exception to leak beyond their containing constructors.
In fewer words, it means that:
If construction of any base or member subobject fails, the whole object's construction must fail.
The difference is that at the end of:
A()
try
{
throw 4;
}
catch(...)
{
cout << "Catch in A()\n";
}
the exception is implicitly rethrown and no object A get constructed, whereas in:
A(int) {
try
{
throw 4;
}
catch(...)
{
cout << "Catch in A(int)\n";
}
}
you swallow the exception and an instance of A is fully constructed.
Destructors run only on fully constructed objects, that is objects whose constructor finished successfully, without throwing an exception.
EDIT: as per the destruction of subobjcets, the catch in the first case is run after the sub-object has been destructed. This is consistent with the member initialization syntax that suggests that it is what should actually happen:
A()
try : ob() // default construct
{
throw 4;
}
catch(...)
{
// here ob is already destructed
cout << "Catch in A()\n";
}
(equivalent to the first case.)
Why that makes a difference on the order of the subobject destruction?
In general, in the function catch clause of A(), you wouldn't know which members had been successfully constructed, because the exception might have come from one of their constructors. So to remove doubt they're destroyed first. Basically the function try/catch is "outside" the data member construction.
Why the exception thrown in A() propagates to main()?
The function catch clause can't make the constructor succeed (because if its members weren't constructed successfully, then the object itself hasn't been constructed successfully). So if you don't throw something else from it then the original exception is rethrown. That's just how it's defined, you can't use a function-try clause to ignore the problem. You can use a regular try/catch inside a function to ignore a problem, then it's up to you to decide whether or not the problem has prevented the object from being correctly constructed.
Looking on the internet for C++ brainteasers, I found this example:
#include <iostream>
using namespace std;
class A {
public:
A()
{
cout << "A::A()" << endl;
}
~A()
{
cout << "A::~A()" << endl;
throw "A::exception";
}
};
class B {
public:
B()
{
cout << "B::B()" << endl;
throw "B::exception"; // <- crashes here
}
~B()
{
cout << "B::~B()";
}
};
int main(int, char**) {
try
{
cout << "Entering try...catch block" << endl;
A objectA;
B objectB;
cout << "Exiting try...catch block" << endl;
}
catch (const char* ex)
{
cout << ex << endl;
}
return 0;
}
This is what I thought the program would do:
A::A() will be output to screen when the constructor of objectA is called. Object A is constructed successfully.
B::B() will be output to screen when the constructor of objectB is called.
The constructor of B then throws an exception. Object B is not constructed successfully.
Destructor of objectB is not called as the constructor never completed successfully.
Destructor of objectA will be called as the object goes out of scope when the try block is exited.
However, when I ran the program, it actually crashed at the line marked with <-. Could anybody explain what exactly was going on at that point?
If you are really coding, not just brainteasing never ever throw exception from destructor. If an exception is thrown during stack-unwinding, terminate() is called. In your case destructor of A has thrown while processing exception that was thrown in B's constructor.
EDIT:
To be more precise (as suggested in comments) - never ever let exception escape destructor. Exceptions that are caught inside destructor make no problem. But if during stack unwinding program has to deal with two exceptions - the one that caused stack unwinding and the one that escaped destructor during unwinding, std::terminate() goes.
When B::B() throws an exception stack unwinding begins. A::~A() is called and throws another exception that is not catched inside A::~A().
If another exception leaves a destructor while stack unwinding is in progress terminate() is called and that looks like a program crash.
Golden rule in C++ - destructors should never throw exceptions. Ignoring this rule will lead to undefined behaviour in all sorts of situations.
The code crashes because B's C'tor throws an exception, and then starts the "Stack Unwinding" procedure: all the local objects are destructed, dus, A's D'tor is called, and throws an exception during the Stack unwinding, and then "abort" is called, because there can't be two exceptions at the same time...
Conclusion:
Never throw an exception from a D'tor, because you might be throwing it during Stack Unwinding.
A::~A() is called when the try block is exited, precisely because the object goes out of scope. This is why RAII works --- it depends on destructors being called regardless of why the scope is exited.
A::~A() then throws an exception. As B::B()'s exception is still being thrown up the stack, this crashes the program.
Your should NEVER throw an exception in destructor. Take a look at this question why.