2

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?
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
sanitizedUser
  • 1,723
  • 3
  • 18
  • 33
  • You may be interested in the address sanitiser, although your username makes me think you might already know about it! – OMGtechy Jul 09 '20 at 12:36
  • 2
    When you assign `Thing 2` to `Thing 0` the `id` member also gets assigned. – IlCapitano Jul 09 '20 at 12:36
  • 1
    If you want to know the exact object that is being created and destroyed print `this` (which will not change for an object), not the `id` that is copied by the copy constructor of `Thing`. – Werner Henze Jul 09 '20 at 12:40
  • @WernerHenze Printing `this` instead of `id` reveals that top`Thing 0` and bottom `Thing 2` is the same object. So no memory leak was created. You can post this as an answer if you want. – sanitizedUser Jul 09 '20 at 12:50
  • 1
    as you use `inline static` which is a c++17 feature, u should definitely use `#include ` – gkhaos Jul 09 '20 at 15:09

2 Answers2

3

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.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
1

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335