El constructor de movimientos en C++

Jinku Hu 12 octubre 2023
El constructor de movimientos en C++

Este artículo presentará cómo utilizar el constructor de movimiento en C++.

Utilice Move Constructor para proporcionar un control de copia eficaz para las clases en C++

El control de copia de la clase define las funciones básicas necesarias para especificar qué sucede cuando el objeto de la clase se copia, mueve, asigna o destruye. Estas funciones tienen una nomenclatura especial de C++, como las funciones copy-constructor y move-constructor definen cómo se inicializa un objeto con otro objeto del mismo tipo. Las funciones de asignación de copia y asignación de movimiento definen cómo se asigna el objeto al mismo tipo de objeto. Destructor maneja la rutina que se ejecuta cuando el objeto sale de su alcance. Es muy probable que el usuario defina algunas de estas funciones, pero si no, el compilador crea los prototipos predeterminados.

Cuando el objeto de clase administra la memoria dinámica y los datos son bastante grandes, las operaciones de copia pueden ser bastante intensivas en computación. Pueden consumir recursos importantes que afectan el rendimiento. Por lo tanto, a menudo utilizan un constructor de movimientos para reasignar datos dinámicos sin copiarlos a la nueva ubicación de memoria. Se logra asignando los punteros del objeto antiguo a los miembros correspondientes del objeto recién inicializado o asignado. Observe que el siguiente ejemplo no incluye el constructor de movimiento y provoca múltiples invocaciones del constructor de copia, que delega al constructor predeterminado.

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

Producción :

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 vez que definimos un constructor de movimiento, que generalmente debería tomar la referencia de valor r como el primer argumento (denotado con la notación &&), la inicialización del vector se vuelve más eficiente a medida que se agregan los nuevos elementos de tipo MyClass. Dado que el constructor de movimiento no asigna nueva memoria y se hace cargo de la ubicación mantenida por el objeto pasado, es necesario asignar nullptr a los miembros del objeto anterior. De lo contrario, el destructor intentará liberar la misma ubicación de memoria dos veces, arrojando el error de tiempo de ejecución.

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

Producción :

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

Artículo relacionado - C++ Class