I have following program:
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyError : public runtime_error
{
public:
MyError(string mess = "");
~MyError(void);
};
MyError::MyError(string mess) : runtime_error(mess)
{
cout << "MyError::MyError()\n";
}
MyError::~MyError(void)
{
cout << "MyError::~MyError\n";
}
int main(void)
{
try {
throw MyError("hi");
}
catch (MyError& exc) {
cout << exc.what() << endl;
}
cout << "goodbye\n";
return 0;
}
Which prints the following:
MyError::MyError()
MyError::~MyError
hi
MyError::~MyError
goodbye
Why is the destructor of the exception (~MyError()) called twice?
I assumed that throw creates a new object, but I do not understand why the class destructor is called.
C++ destructors are called twice for local objects when exiting a function via a nested return statement.
Why is the destructor being called three times?
No. You can have only one destructor for a class Fred . It's always called Fred::~Fred() .
When an exception is thrown, destructors of the objects (whose scope ends with the try block) are automatically called before the catch block gets executed.
If you instrument the exception's copy or move constructor, you'll find it's called once before the handler. There's a temporary exception object into which the thrown expression is copied/moved, and it is this exception object to which the reference in the handler will bind. C++14 15.1/3+
So the execution resulting from your code looks something like this (pseudo-C++):
// throw MyError("hi"); expands to:
auto tmp1 = MyError("hi");
auto exceptionObject = std::move(tmp1);
tmp1.~MyError();
goto catch;
// catch expands to:
MyError& exc = exceptionObject;
cout << exc.what() << endl;
// } of catch handler expands to:
exceptionObject.~MyError();
// normal code follows:
cout << "goodbye\n";
Because your compiler is failing to elide the copy from the temporary to the exception object managed by the exception handling mechanism.
Conceptually, MyError("hi")
creates a temporary, which will be destroyed at the end of the statement (before the exception can be handled). throw
copies the thrown value somewhere else, where it will persist until after the exception has been handled. If the thrown value is a temporary, then a decent compiler should elide the copy and initialise the thrown value directly; apparently, your compiler didn't do that.
My compiler (GCC 4.8.1) does a rather better job:
MyError::MyError()
hi
MyError::~MyError
goodbye
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With