Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the destructor of an exception called twice?

Tags:

c++

destructor

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.

like image 307
TiPo Avatar asked May 18 '15 17:05

TiPo


People also ask

Why is my destructor being called twice?

C++ destructors are called twice for local objects when exiting a function via a nested return statement.

How many times is a destructor called?

Why is the destructor being called three times?

Is destructor called only once?

No. You can have only one destructor for a class Fred . It's always called Fred::~Fred() .

Are destructors called on exception?

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.


2 Answers

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";
like image 164
Angew is no longer proud of SO Avatar answered Oct 27 '22 01:10

Angew is no longer proud of SO


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
like image 20
Mike Seymour Avatar answered Oct 27 '22 01:10

Mike Seymour