Does object have to be destroyed when a new object is assigned to its address?

Viewed 185

Consider following code.

#include <stdio.h>
using namespace std;

constexpr size_t TOTAL = 2;

class thing
{
private:
    inline static size_t NEXT_ID = 0;
    size_t id;

public:
    thing() : id(NEXT_ID++)
    {
        printf("Thing %zd created.\n", this->id);
    }
    ~thing()
    {
        printf("Thing %zd destroyed.\n", this->id);
    }
};

class container
{
private:
    inline static size_t NEXT_ID = 0;
    size_t id;
    thing* things;

public:
    container() : id(NEXT_ID++)
    {
        this->things = new thing[TOTAL];
        printf("Container %zd created.\n", this->id);
    }
    ~container()
    {
        delete[] this->things;
        printf("Container %zd destroyed.\n", this->id);
    }

    thing& at(size_t idx)    // this is the important method
    {
        return this->things[idx];
    }
};

int main()
{
    container c;
    c.at(0) = thing();   // here is the problem
    return 0;
}

The output is something I didn't expect.

Thing 0 created.
Thing 1 created.
Container 0 created.
Thing 2 created.
Thing 2 destroyed.
Thing 1 destroyed.
Thing 2 destroyed.
Container 0 destroyed.

I know that Thing 2 was a temporary object, that's why it was destroyed twice. I have a few questions about what happened to Thing 0.

  • Why wasn't Thing 0 destroyed?
  • Will there be a memory leak?
  • Do I have to destroy Thing 0 somehow or was it overwritten successfully?
2 Answers

There is no double call to a destructor for the same object. The problem is only in your output. You are printing the id but the copy assignment in c.at(0) = thing(); copies the id from the temporary object to the one in the container. That is the reason that you see two "Thing 2 destroyed." and no "Thing 0 destroyed.".

If you want a better logging mechanism you can print the this pointer. The address of an object does not change over the lifetime of an object, it is a unique identifier for an object. Of course for convenience you can additionally print the id.

printf("Thing %p %zd created.\n", static_cast<void*>(this), this->id);
printf("Thing %p %zd destroyed.\n", static_cast<void*>(this), this->id);

That should give you some output like this (of course 0x11111111, 0x22222222 and 0x33333333 will look different in your case):

Thing 0x11111111 0 created.
Thing 0x22222222 1 created.
Container 0 created.
Thing 0x33333333 2 created.
Thing 0x33333333 2 destroyed.
Thing 0x22222222 1 destroyed.
Thing 0x11111111 2 destroyed.
Container 0 destroyed.

In this statement

c.at(0) = thing();

there is used the implicitly defined by the compiler copy assignment operator. So the data member id of the object referenced by the expression c.at(0) becomes equal to the id of the temporary object created by the expression thing() that is equal to 2.

In this statement the temporary object is created and at the end is destroyed

Thing 2 created.
Thing 2 destroyed.

Now the object c contains two sub-objects thing stored as elements of an array. The sub-objects have id(s) 2 and 1.

They are deleted in the reverse order relative to creating of them

Thing 1 destroyed.
Thing 2 destroyed.  // previously it has id equal to 0

So the program does not have a memory leak. All created objects were successfully deleted as it is seen from the program output.

Related