Reputation: 436
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.
Upvotes: 14
Views: 832
Reputation: 254411
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
Upvotes: 4
Reputation: 490008
Your exception is being copied. If you instrument the copy ctor, you can see this:
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyError : public runtime_error
{
public:
MyError(MyError const &e) : runtime_error("copy") { std::cout << "Copy MyError"; }
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;
}
Result:
MyError::MyError()
Copy MyError
MyError::~MyError
copy.what()
MyError::~MyError
goodbye
Upvotes: 3
Reputation: 171097
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";
Upvotes: 11