Le constructeur de déplacement en C++

Jinku Hu 12 octobre 2023
Le constructeur de déplacement en C++

Cet article présentera comment utiliser le constructeur de déplacement en C++.

Utilisez le constructeur Move pour fournir un contrôle de copie efficace pour les classes en C++

Le contrôle de copie de la classe définit les fonctions de base nécessaires pour spécifier ce qui se passe lorsque l’objet de classe est copié, déplacé, affecté ou détruit. Ces fonctions ont une nomenclature C++ spéciale, comme les fonctions de constructeur par copie et de constructeur par déplacement qui définissent comment un objet est initialisé avec un autre objet du même type. Les fonctions d’affectation de copie et d’affectation de déplacement définissent comment l’objet est affecté au même type d’objet. Destructor gère la routine qui s’exécute lorsque l’objet sort de la portée. Certaines de ces fonctions sont plus susceptibles d’être définies par l’utilisateur, mais sinon, le compilateur crée lui-même les prototypes par défaut.

Lorsque l’objet de classe gère la mémoire dynamique et que les données sont assez volumineuses, les opérations de copie peuvent être assez gourmandes en calcul. Ils peuvent utiliser des ressources importantes affectant les performances. Ainsi, ils utilisent souvent un constructeur de déplacement pour réaffecter des données dynamiques sans les copier dans le nouvel emplacement mémoire. Il est réalisé en affectant les pointeurs de l’ancien objet aux membres correspondants de l’objet nouvellement initialisé ou affecté. Notez que l’exemple suivant n’inclut pas le constructeur de déplacement et qu’il provoque plusieurs appels du constructeur de copie, qui délègue au constructeur par défaut.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

Production:

Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called

Une fois que nous avons défini un constructeur de déplacement, qui devrait généralement prendre la référence de valeur r comme premier argument (indiqué par la notation &&), l’initialisation vectorielle devient plus efficace à mesure que les nouveaux éléments de type MyClass sont ajoutés. Étant donné que le constructeur de déplacement n’alloue pas de nouvelle mémoire et prend en charge l’emplacement détenu par l’objet passé, il faut affecter nullptr aux membres de l’objet précédent. Sinon, le destructeur essaiera de libérer deux fois le même emplacement mémoire, en générant l’erreur d’exécution.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  MyClass(MyClass&& source) noexcept : data(source.data) {
    source.data = nullptr;
    cout << "Move Constructor is called" << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

Production:

Constructor 1 is called
Move Constructor is called
Destructor is called
Constructor 1 is called
Move Constructor is called
Move Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called
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