Pointeur unique - Pourquoi le destructeur est-il appelé 3 fois

Pointeur unique - Pourquoi le destructeur est-il appelé 3 fois

Il y a en effet 3 fois qu'une instance de Bla est construit.

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

Ne revenez pas par déménagement. Renvoyez simplement bla , dans la plupart des cas, la copie sera élidée.

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

Notez que make_unique<Bla> construit toujours une nouvelle instance. Dans ce cas, parce que vous passez une autre instance, cela devient une construction de copie.

Un indice que la construction de copie a lieu est que votre constructeur par défaut n'est invoqué qu'une seule fois, tandis que le destructeur est invoqué 3 fois. C'est parce que dans les 2 autres cas, le constructeur implicite de copie (ou de déplacement) est invoqué (Bla::Bla(Bla const&) ).


Le compilateur peut même vous avertir que

Je ne suis pas sûr à 100 %, mais je pense que vous recevez les trois appels de desctructor de :

  • La variable locale bla à partir de GetBla()
  • La valeur de retour de GetBla() après son utilisation en std::make_unique<Bla>(GetBla());
  • Évidemment du destructeur du std::unique_ptr

Le plus simple est de laisser std::make_uniqe appeler le constructeur par défaut de 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>();
}

Sortie

Constructor!
Destructor!

La bonne façon de créer unique_ptr :

auto bla = std::make_unique<Bla>();

Cependant, votre code crée 3 instances de Bla :

  1. Objet local bla en GetBla() fonction.
  2. Valeur de retour de GetBla() .
  3. Enfin, make_unique() crée une instance supplémentaire.

REMARQUE :

  1. En présence d'un destructeur défini par l'utilisateur, le compilateur ne génère pas de constructeur de déplacement, donc GetBla() la valeur de retour est une copie de l'objet local bla .
  2. Depuis GetBla() renvoie move 'objet local, l'élision de copie est supprimée.