Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unique pointer - Why is the destructor called 3 times

Tags:

c++

unique-ptr

I have a method that returns an object by value. The method comes from a library that I have no control over. For the further handling of the object I want to continue working with a unique_ptr on this object. Here is an example:

#include <iostream>
#include <memory>

class Bla {
  public:
      Bla() { std::cout << "Constructor!\n"; }
      ~Bla() { std::cout << "Destructor!\n"; }
};

Bla GetBla() {
  Bla bla;
  return std::move(bla);
}

int main() {
  auto bla = std::make_unique<Bla>(GetBla());
}

The example produces the following output:

Constructor!
Destructor!
Destructor!
Destructor!

Why is the destructor of Bla called 3 times here? Is the way I create the unique_prt correct?

like image 336
Tobi S. Avatar asked Mar 02 '20 10:03

Tobi S.


2 Answers

There are indeed 3 times that an instance of Bla is constructed.

Bla GetBla() {
  Bla bla;    // 1st construction
  return std::move(bla); // 2nd construction (return by copy)
}

Don't return by move. Just return bla, in most cases the copy will be elided.

  auto bla = std::make_unique<Bla>(GetBla());  // 3rd construction - Bla copy construction

Note that make_unique<Bla> always constructs a new instance. In this case because you're passing another instance, it becomes copy-construction.

A hint that copy construction takes place is that your default constructor is invoked only once, while the destructor is invoked 3 times. That's because in the other 2 cases the implicit copy (or move) constructor is invoked (Bla::Bla(Bla const&)).

like image 79
rustyx Avatar answered Sep 29 '22 13:09

rustyx


The compiler may even warn you that

moving a local object in a return statement prevents copy elision.

I'm not 100% sure, but I think you get the three desctructor calls from:

  • The local variable bla from GetBla()
  • The return value from GetBla() after it was used in std::make_unique<Bla>(GetBla());
  • Obviously from the destructor of the std::unique_ptr

The easiest way is to let std::make_uniqe invoke the default constructor of Bla:

auto bla = std::make_unique<Bla>(); // Calls Bla::Bla() to initalize the owned object
#include <iostream>
#include <memory>

class Bla {
  public:
      Bla() { std::cout << "Constructor!\n"; }
      ~Bla() { std::cout << "Destructor!\n"; }
};

int main() {
  auto bla = std::make_unique<Bla>();
}

Output

Constructor!
Destructor!
like image 41
churill Avatar answered Sep 29 '22 14:09

churill