Constructores de movimiento y operadores de asignación de movimiento (C++)Move Constructors and Move Assignment Operators (C++)

En este tema se describe cómo escribir un constructor de movimiento y un operador de asignación de movimiento para una clase de C++.This topic describes how to write a move constructor and a move assignment operator for a C++ class. Un constructor de movimiento permite que los recursos que pertenecen a un objeto rvalue se muevan a un valor l sin copiar.A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. Para obtener más información sobre la semántica de movimiento, vea declarador de referencia de valor r: &&.For more information about move semantics, see Rvalue Reference Declarator: &&.

Este tema se basa en la siguiente clase de C++, MemoryBlock, que administra un búfer de memoria.This topic builds upon the following C++ class, MemoryBlock, which manages a memory buffer.

// MemoryBlock.h
#pragma once
#include <iostream>
#include <algorithm>

class MemoryBlock
{
public:

   // Simple constructor that initializes the resource.
   explicit MemoryBlock(size_t length)
      : _length(length)
      , _data(new int[length])
   {
      std::cout << "In MemoryBlock(size_t). length = "
                << _length << "." << std::endl;
   }

   // Destructor.
   ~MemoryBlock()
   {
      std::cout << "In ~MemoryBlock(). length = "
                << _length << ".";

      if (_data != nullptr)
      {
         std::cout << " Deleting resource.";
         // Delete the resource.
         delete[] _data;
      }

      std::cout << std::endl;
   }

   // Copy constructor.
   MemoryBlock(const MemoryBlock& other)
      : _length(other._length)
      , _data(new int[other._length])
   {
      std::cout << "In MemoryBlock(const MemoryBlock&). length = "
                << other._length << ". Copying resource." << std::endl;

      std::copy(other._data, other._data + _length, _data);
   }

   // Copy assignment operator.
   MemoryBlock& operator=(const MemoryBlock& other)
   {
      std::cout << "In operator=(const MemoryBlock&). length = "
                << other._length << ". Copying resource." << std::endl;

      if (this != &other)
      {
         // Free the existing resource.
         delete[] _data;

         _length = other._length;
         _data = new int[_length];
         std::copy(other._data, other._data + _length, _data);
      }
      return *this;
   }

   // Retrieves the length of the data resource.
   size_t Length() const
   {
      return _length;
   }

private:
   size_t _length; // The length of the resource.
   int* _data; // The resource.
};

Los procedimientos siguientes describen cómo escribir un constructor de movimiento y un operador de asignación de movimiento para la clase de C++ de ejemplo.The following procedures describe how to write a move constructor and a move assignment operator for the example C++ class.

Para crear un constructor de movimiento para una clase de C++To create a move constructor for a C++ class

  1. Defina un método de constructor vacío que tome una referencia de valor R al tipo de clase como su parámetro, como se muestra en el ejemplo siguiente:Define an empty constructor method that takes an rvalue reference to the class type as its parameter, as demonstrated in the following example:

    MemoryBlock(MemoryBlock&& other)
       : _data(nullptr)
       , _length(0)
    {
    }
    
  2. En el constructor de movimiento, asigne los miembros de datos de clase del objeto de origen al objeto que se está construyendo:In the move constructor, assign the class data members from the source object to the object that is being constructed:

    _data = other._data;
    _length = other._length;
    
  3. Asigne los miembros de datos del objeto de origen a valores predeterminados.Assign the data members of the source object to default values. Esto evita que el destructor libere varias veces los recursos (tales como la memoria):This prevents the destructor from freeing resources (such as memory) multiple times:

    other._data = nullptr;
    other._length = 0;
    

Para crear un operador de asignaciones de movimiento para una clase de C++To create a move assignment operator for a C++ class

  1. Defina un operador de asignación vacío que tome una referencia de valor R al tipo de clase como su parámetro y devuelva una referencia al tipo de clase, como se muestra en el ejemplo siguiente:Define an empty assignment operator that takes an rvalue reference to the class type as its parameter and returns a reference to the class type, as demonstrated in the following example:

    MemoryBlock& operator=(MemoryBlock&& other)
    {
    }
    
  2. En el operador de asignación de movimiento, agregue una instrucción condicional que no realice ninguna operación si intenta asignar el objeto a sí mismo.In the move assignment operator, add a conditional statement that performs no operation if you try to assign the object to itself.

    if (this != &other)
    {
    }
    
  3. En la instrucción condicional, libere los recursos (tales como la memoria) del objeto al que se asigna.In the conditional statement, free any resources (such as memory) from the object that is being assigned to.

    El ejemplo siguiente libera el miembro _data del objeto al que se asigna:The following example frees the _data member from the object that is being assigned to:

    // Free the existing resource.
    delete[] _data;
    

    Siga los pasos 2 y 3 del primer procedimiento para transferir los miembros de datos del objeto de origen al objeto que se construye:Follow steps 2 and 3 in the first procedure to transfer the data members from the source object to the object that is being constructed:

    // Copy the data pointer and its length from the
    // source object.
    _data = other._data;
    _length = other._length;
    
    // Release the data pointer from the source object so that
    // the destructor does not free the memory multiple times.
    other._data = nullptr;
    other._length = 0;
    
  4. Devuelva una referencia al objeto actual, como se muestra en el ejemplo siguiente:Return a reference to the current object, as shown in the following example:

    return *this;
    

Ejemplo: completar el constructor de movimiento y el operador de asignaciónExample: Complete move constructor and assignment operator

En el ejemplo siguiente se muestra el constructor de movimiento y el operador de asignación de movimiento completos para la clase MemoryBlock:The following example shows the complete move constructor and move assignment operator for the MemoryBlock class:

// Move constructor.
MemoryBlock(MemoryBlock&& other) noexcept
   : _data(nullptr)
   , _length(0)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = "
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the
   // source object.
   _data = other._data;
   _length = other._length;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = nullptr;
   other._length = 0;
}

// Move assignment operator.
MemoryBlock& operator=(MemoryBlock&& other) noexcept
{
   std::cout << "In operator=(MemoryBlock&&). length = "
             << other._length << "." << std::endl;

   if (this != &other)
   {
      // Free the existing resource.
      delete[] _data;

      // Copy the data pointer and its length from the
      // source object.
      _data = other._data;
      _length = other._length;

      // Release the data pointer from the source object so that
      // the destructor does not free the memory multiple times.
      other._data = nullptr;
      other._length = 0;
   }
   return *this;
}

Ejemplo de uso de la semántica de movimiento para mejorar el rendimientoExample Use move semantics to improve performance

El ejemplo siguiente muestra cómo la semántica de transferencia de recursos puede mejorar el rendimiento de las aplicaciones.The following example shows how move semantics can improve the performance of your applications. El ejemplo agrega dos elementos a un objeto vectorial y después inserta un nuevo elemento entre los dos existentes.The example adds two elements to a vector object and then inserts a new element between the two existing elements. La vector clase usa la semántica de movimiento para realizar la operación de inserción eficazmente moviendo los elementos del vector en lugar de copiarlos.The vector class uses move semantics to perform the insertion operation efficiently by moving the elements of the vector instead of copying them.

// rvalue-references-move-semantics.cpp
// compile with: /EHsc
#include "MemoryBlock.h"
#include <vector>

using namespace std;

int main()
{
   // Create a vector object and add a few elements to it.
   vector<MemoryBlock> v;
   v.push_back(MemoryBlock(25));
   v.push_back(MemoryBlock(75));

   // Insert a new element into the second position of the vector.
   v.insert(v.begin() + 1, MemoryBlock(50));
}

Este ejemplo produce el siguiente resultado:This example produces the following output:

In MemoryBlock(size_t). length = 25.
In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.
In ~MemoryBlock(). length = 0.
In MemoryBlock(size_t). length = 75.
In MemoryBlock(MemoryBlock&&). length = 75. Moving resource.
In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.
In ~MemoryBlock(). length = 0.
In ~MemoryBlock(). length = 0.
In MemoryBlock(size_t). length = 50.
In MemoryBlock(MemoryBlock&&). length = 50. Moving resource.
In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.
In MemoryBlock(MemoryBlock&&). length = 75. Moving resource.
In ~MemoryBlock(). length = 0.
In ~MemoryBlock(). length = 0.
In ~MemoryBlock(). length = 0.
In ~MemoryBlock(). length = 25. Deleting resource.
In ~MemoryBlock(). length = 50. Deleting resource.
In ~MemoryBlock(). length = 75. Deleting resource.

Antes de Visual Studio 2010, este ejemplo genera el siguiente resultado:Before Visual Studio 2010, this example produced the following output:

In MemoryBlock(size_t). length = 25.
In MemoryBlock(const MemoryBlock&). length = 25. Copying resource.
In ~MemoryBlock(). length = 25. Deleting resource.
In MemoryBlock(size_t). length = 75.
In MemoryBlock(const MemoryBlock&). length = 25. Copying resource.
In ~MemoryBlock(). length = 25. Deleting resource.
In MemoryBlock(const MemoryBlock&). length = 75. Copying resource.
In ~MemoryBlock(). length = 75. Deleting resource.
In MemoryBlock(size_t). length = 50.
In MemoryBlock(const MemoryBlock&). length = 50. Copying resource.
In MemoryBlock(const MemoryBlock&). length = 50. Copying resource.
In operator=(const MemoryBlock&). length = 75. Copying resource.
In operator=(const MemoryBlock&). length = 50. Copying resource.
In ~MemoryBlock(). length = 50. Deleting resource.
In ~MemoryBlock(). length = 50. Deleting resource.
In ~MemoryBlock(). length = 25. Deleting resource.
In ~MemoryBlock(). length = 50. Deleting resource.
In ~MemoryBlock(). length = 75. Deleting resource.

La versión de este ejemplo que usa semántica de movimiento de recursos es más eficiente que la versión que no la usa, porque realiza menos operaciones de copia, asignación de memoria y desasignación de memoria.The version of this example that uses move semantics is more efficient than the version that does not use move semantics because it performs fewer copy, memory allocation, and memory deallocation operations.

Programación sólidaRobust Programming

Para evitar pérdidas de recursos, libere siempre los recursos (tales como memoria, identificadores de archivos y sockets) en el operador de asignación de movimiento.To prevent resource leaks, always free resources (such as memory, file handles, and sockets) in the move assignment operator.

Para evitar la destrucción irrecuperable de recursos, administre correctamente la autoasignación en el operador de asignación de movimiento.To prevent the unrecoverable destruction of resources, properly handle self-assignment in the move assignment operator.

Si proporciona tanto un constructor de movimiento como un operador de asignación de movimiento para la clase, puede eliminar código redundante escribiendo el constructor de movimiento para llamar al operador de asignación de movimiento.If you provide both a move constructor and a move assignment operator for your class, you can eliminate redundant code by writing the move constructor to call the move assignment operator. En el ejemplo siguiente se muestra una versión revisada del constructor de movimiento que llama al operador de asignación de movimiento:The following example shows a revised version of the move constructor that calls the move assignment operator:

// Move constructor.
MemoryBlock(MemoryBlock&& other) noexcept
   : _data(nullptr)
   , _length(0)
{
   *this = std::move(other);
}

La función STD:: Move convierte el valor l other en un valor r.The std::move function converts the lvalue other to an rvalue.

Consulta tambiénSee also

Declarador de referencias rvalue: &&Rvalue Reference Declarator: &&
STD:: Movestd::move