Il costruttore di mosse in C++

Jinku Hu 12 ottobre 2023
Il costruttore di mosse in C++

Questo articolo introdurrà come usare il costruttore di spostamento in C++.

Utilizzare il costruttore Move per fornire un controllo di copia efficiente per le classi in C++

Il controllo della copia della classe definisce le funzioni principali necessarie per specificare cosa succede quando l’oggetto classe viene copiato, spostato, assegnato o distrutto. Queste funzioni hanno una nomenclatura C++ speciale, come le funzioni copy-constructor e move-constructor che definiscono come un oggetto viene inizializzato con un altro oggetto dello stesso tipo. le funzioni di assegnazione di copia e assegnazione di spostamento definiscono come l’oggetto viene assegnato allo stesso tipo di oggetto. Destructor gestisce la routine che viene eseguita quando l’oggetto esce dall’ambito. È più probabile che alcune di queste funzioni vengano definite dall’utente, ma in caso contrario, il compilatore stesso crea i prototipi predefiniti.

Quando l’oggetto classe gestisce la memoria dinamica e i dati sono piuttosto grandi, le operazioni di copia possono essere piuttosto complesse. Possono consumare risorse significative che influiscono sulle prestazioni. Pertanto, spesso utilizzano un costruttore di mosse per riassegnare i dati dinamici senza copiarli nella nuova posizione di memoria. Si ottiene assegnando i puntatori del vecchio oggetto ai membri corrispondenti dell’oggetto appena inizializzato o assegnato. Si noti che l’esempio seguente non include il costruttore di spostamento e provoca più chiamate del costruttore di copia, che delega al costruttore predefinito.

#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;
}

Produzione:

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

Una volta definito un costruttore di mosse, che generalmente dovrebbe prendere il riferimento al valore r come primo argomento (indicato con la notazione &&), l’inizializzazione del vettore diventa più efficiente man mano che vengono aggiunti i nuovi elementi di tipo MyClass. Poiché il costruttore di mosse non alloca nuova memoria e prende il posto dell’oggetto passato, è necessario assegnare nullptr ai membri dell’oggetto precedente. In caso contrario, il distruttore tenterà di liberare due volte la stessa posizione di memoria, generando l’errore di runtime.

#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;
}

Produzione:

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
Autore: 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

Articolo correlato - C++ Class