c++destructorautomatic-storage

Why is automatic object's destructor called twice?


(The answer to my question involves copy constructors, but the copy takes place upon return from a function, not within a method call to another class. I actually saw the referenced possible duplicate, but did not infer from the copy made by vector::push_back that my function here also made a copy. Perhaps I should have.)

I am trying to understand the construction/destruction of automatic objects. I ran into some code that looked dubious to me, so I wrote my own version in an effort to understand it. In short, the original code included a function that returned an object that was local to the function (an automatic). That looked unsafe to me, so I wrote this program to explore it:

#include <stdio.h>

class Phantom
{
private:
    static int counter;
    int id;

public:
    Phantom()
    {
        ++counter;
        id = counter;
        printf("Phantom %d constructed.\n", id);
    };

    virtual ~Phantom()
    {
        printf("Phantom %d destructed.\n", id);
    };

    void speak()
    {
        printf("Phantom %d speaks.\n", id);
    };
};

int Phantom::counter = 0;

Phantom getPhantom()
{
    Phantom autoPhantom;

    return autoPhantom; // THIS CAN'T BE SAFE
}

int main()
{
    Phantom phantom;

    phantom = getPhantom();

    phantom.speak();

    return 0;
}

I get this output:

Phantom 1 constructed.
Phantom 2 constructed.
Phantom 2 destructed.
Phantom 2 destructed.
Phantom 2 speaks.

It's the fourth line in the output that confuses me.

Phantom 1 is constructed automatically when main is entered.

Phantom 2 is constructed automatically when getPhantom is entered.

Phantom 2 is destructed automatically when getPhantom is exited (which is why I believe returning it from getPhantom is unsafe).

But after that I'm confused. According to the debugger, getPhantom has returned before the fourth line of output appears. When Phantom's destructor is called the second time, the call stack is this:

main
~Phantom

In a managed language, I could see how this line:

phantom = getPhantom();

would destroy Phantom 1, but it wouldn't touch Phantom 2. And this is C++, not Java.

What causes the second call to Phantom 2's destructor?


Solution

  • You return a copy. Therefore the variable in getPhantom() is destroyed at the end of the scope and you are left with its copy that has also id 2. It is because on return it calls copy constructor (also default one) that does not increment the id.