Appeler un destructeur explicitement en C++

Jinku Hu 12 octobre 2023
Appeler un destructeur explicitement en C++

Cet article explique plusieurs méthodes pour appeler un destructeur explicitement en C++.

Utilisez la notation obj.~ClassName() pour appeler explicitement une fonction destructrice

Les destructeurs sont des fonctions spéciales qui sont exécutées lorsqu’un objet sort automatiquement de la portée ou est supprimé par un appel explicite de l’utilisateur. Notez que ces fonctions sont généralement utilisées pour libérer les ressources utilisées par l’objet donné. Même si un destructeur peut être appelé explicitement en tant que fonction membre, il n’est pas nécessaire de le faire. Dans la plupart des cas, lorsque les membres de données de classe sont alloués dynamiquement, cela peut conduire à une double libération des ressources. Ce dernier scénario entraîne généralement une interruption anormale du programme.

Dans l’exemple suivant, nous démontrons notre classe définie appelée - MyClass, qui a deux constructeurs et une méthode intégrée pour récupérer la seule valeur du membre de données. Le destructeur est également défini, et avec les constructeurs, ils affichent les messages correspondants dans le flux cout pour nous faciliter la recherche du comportement.

Notez que l’exemple de code suivant imprime deux messages de destructeur, dont l’un est déclenché par l’appel utilisateur explicite et l’autre est automatiquement appelé à la sortie du programme. Bien que, si le membre de données MyClass avait été alloué avec l’opérateur new dans le constructeur, cet exemple aurait conduit à une fin anormale du programme - probablement une faute sans double.

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
 public:
  explicit MyClass(string s) : str(std::move(s)) {
    cout << "Constructor 1 executed\n";
  }

  MyClass(const MyClass& s) : str(string(s.str)) {
    cout << "Constructor 2 executed\n";
  }

  ~MyClass() { cout << "Destructor executed\n"; }

  string& getString() { return str; };

 private:
  string str;
};

int main() {
  MyClass str1("Hello There! ");

  cout << endl;
  cout << "str1: " << str1.getString() << endl;
  cout << endl;

  str1.~MyClass();

  return EXIT_SUCCESS;
}

Production:

Constructor 1 executed

str1: Hello There!

Destructor executed
Destructor executed

Alternativement, nous pouvons voir que la version suivante du code déclenche le même nombre de fonctions constructeur / destructeur, ce qui est essentiellement l’idée derrière ce concept. Ainsi, même si le dernier exemple fonctionne sans aucune faute, il n’est pas recommandé d’appeler explicitement les destructeurs.

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
 public:
  explicit MyClass(string s) : str(std::move(s)) {
    cout << "Constructor 1 executed\n";
  }

  MyClass(const MyClass& s) : str(string(s.str)) {
    cout << "Constructor 2 executed\n";
  }

  ~MyClass() { cout << "Destructor executed\n"; }

  string& getString() { return str; };

 private:
  string str;
};

int main() {
  MyClass str1("Hello There! ");
  MyClass str2(str1);

  cout << endl;
  cout << "str2: " << str2.getString() << endl;
  cout << endl;

  return EXIT_SUCCESS;
}

Production:

Constructor 1 executed
Constructor 2 executed

str2: Hello There!

Destructor executed
Destructor executed

Un autre cas à considérer est lorsque l’objet de classe est alloué avec l’opérateur new, et avant la sortie du programme, le delete est appelé sur le même objet. Notez que la dernière instruction cout est imprimée après l’exécution de la fonction descripteur, c’est-à-dire que le descripteur a été invoqué lors de l’appel de l’opérateur delete.

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class MyClass {
 public:
  explicit MyClass(string s) : str(std::move(s)) {
    cout << "Constructor 1 executed\n";
  }

  MyClass(const MyClass& s) : str(string(s.str)) {
    cout << "Constructor 2 executed\n";
  }

  ~MyClass() { cout << "Destructor executed\n"; }

  string& getString() { return str; };
  string* getStringAddr() { return &str; };

 private:
  string str;
};

int main() {
  auto* str4 = new MyClass("Hello There! ");

  cout << endl;
  cout << "str4: " << str4->getString() << endl;
  cout << endl;

  delete str4;
  cout << "exiting" << endl;

  return EXIT_SUCCESS;
}

Production:

Constructor 1 executed

str4: Hello There!

Destructor executed
Auteur: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Article connexe - C++ Class