Mover constructores en C++ con ejemplos

Requisitos previos: referencias de valor l y valor r en C++ , Copiar constructor en C++ .

¿Qué es un constructor de movimiento?  

Los constructores de copia en C++ funcionan con las referencias de valor l y la semántica de copia (la semántica de copia significa copiar los datos reales del objeto a otro objeto en lugar de hacer que otro objeto señale el objeto ya existente en el montón). Mientras que los constructores de movimiento trabajan en las referencias de valor r y semántica de movimiento (la semántica de movimiento implica apuntar al objeto ya existente en la memoria).

Al declarar el nuevo objeto y asignarle el valor r, primero se crea un objeto temporal y luego ese objeto temporal se usa para asignar los valores al objeto. Debido a esto, el constructor de copias se llama varias veces y aumenta la sobrecarga y disminuye la potencia computacional del código. Para evitar esta sobrecarga y hacer que el código sea más eficiente, usamos constructores de movimiento.

¿Por qué se utilizan los constructores de movimiento?

El constructor de movimiento mueve los recursos en el montón, es decir, a diferencia de los constructores de copia que copian los datos del objeto existente y lo asignan al nuevo objeto, el constructor de movimiento simplemente hace que el puntero del objeto declarado apunte a los datos del objeto temporal y lo anula. el puntero de los objetos temporales. Por lo tanto, el constructor de movimiento evita la copia innecesaria de datos en la memoria.  

El constructor de trabajo de movimiento se parece un poco al constructor de copia de miembro predeterminado, pero en este caso, anula el puntero del objeto temporal, lo que impide que más de un objeto apunte a la misma ubicación de memoria.

A continuación se muestra el programa sin declarar el constructor de movimiento:

C++

// C++ program without declaring the
// move constructor
#include <iostream>
#include <vector>
using namespace std;
 
// Move Class
class Move {
private:
    // Declaring the raw pointer as
    // the data member of the class
    int* data;
 
public:
    // Constructor
    Move(int d)
    {
        // Declare object in the heap
        data = new int;
        *data = d;
 
        cout << "Constructor is called for "
             << d << endl;
    };
 
    // Copy Constructor to delegated
    // Copy constructor
    Move(const Move& source)
        : Move{ *source.data }
    {
 
        // Copying constructor copying
        // the data by making deep copy
        cout << "Copy Constructor is called - "
             << "Deep copy for "
             << *source.data
             << endl;
    }
 
    // Destructor
    ~Move()
    {
        if (data != nullptr)
 
            // If the pointer is not
            // pointing to nullptr
            cout << "Destructor is called for "
                 << *data << endl;
        else
 
            // If the pointer is
            // pointing to nullptr
            cout << "Destructor is called"
                 << " for nullptr"
                 << endl;
 
        // Free the memory assigned to
        // data member of the object
        delete data;
    }
};
 
// Driver Code
int main()
{
    // Create vector of Move Class
    vector<Move> vec;
 
    // Inserting object of Move class
    vec.push_back(Move{ 10 });
    vec.push_back(Move{ 20 });
    return 0;
}
Producción: 

Constructor is called for 10
Constructor is called for 10
Copy Constructor is called - Deep copy for 10
Destructor is called for 10
Constructor is called for 20
Constructor is called for 20
Copy Constructor is called - Deep copy for 20
Constructor is called for 10
Copy Constructor is called - Deep copy for 10
Destructor is called for 10
Destructor is called for 20
Destructor is called for 10
Destructor is called for 20

 

Explicación: 
el programa anterior muestra el constructor de copia que llama innecesariamente y el uso ineficiente de la memoria al copiar los mismos datos varias veces como nuevo objeto en cada llamada al constructor de copia.

Sintaxis del constructor de movimiento:

Object_name(Object_name&& obj) 
   : data{ obj.data }
{
   // Nulling out the pointer to the temporary data
   obj.data = nullptr;
}

Este uso innecesario de la memoria se puede evitar utilizando el constructor de movimientos. A continuación se muestra el programa que declara el constructor de movimiento:

C++

// C++ program with declaring the
// move constructor
#include <iostream>
#include <vector>
using namespace std;
 
// Move Class
class Move {
private:
    // Declare the raw pointer as
    // the data member of class
    int* data;
 
public:
   
    // Constructor
    Move(int d)
    {
        // Declare object in the heap
        data = new int;
        *data = d;
        cout << "Constructor is called for "
             << d << endl;
    };
 
    // Copy Constructor
    Move(const Move& source)
        : Move{ *source.data }
    {
 
        // Copying the data by making
        // deep copy
        cout << "Copy Constructor is called -"
             << "Deep copy for "
             << *source.data
             << endl;
    }
 
    // Move Constructor
    Move(Move&& source)
        : data{ source.data }
    {
 
        cout << "Move Constructor for "
             << *source.data << endl;
        source.data = nullptr;
    }
 
    // Destructor
    ~Move()
    {
        if (data != nullptr)
 
            // If pointer is not pointing
            // to nullptr
            cout << "Destructor is called for "
                 << *data << endl;
        else
 
            // If pointer is pointing
            // to nullptr
            cout << "Destructor is called"
                 << " for nullptr "
                 << endl;
 
        // Free up the memory assigned to
        // The data member of the object
        delete data;
    }
};
 
// Driver Code
int main()
{
    // Vector of Move Class
    vector<Move> vec;
 
    // Inserting Object of Move Class
    vec.push_back(Move{ 10 });
    vec.push_back(Move{ 20 });
    return 0;
}

Producción:

Constructor is called for 10
Move Constructor for 10
Destructor is called for nullptr 
Constructor is called for 20
Move Constructor for 20
Constructor is called for 10
Copy Constructor is called -Deep copy for 10
Destructor is called for 10
Destructor is called for nullptr 
Destructor is called for 10
Destructor is called for 20

Explicación: 
La llamada innecesaria al constructor de copia se evita haciendo la llamada al constructor de movimiento. Por lo tanto, hace que el código sea más eficiente con la memoria y disminuye la sobrecarga de llamar al constructor de movimiento.

Publicación traducida automáticamente

Artículo escrito por justanotaku y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *