Compartir a través de


Cómo: escriba un movimiento Constructor

Este tema describe cómo escribir una mover constructor y un operador de asignación de movimiento para una clase de C++.Un constructor de movimiento permite implementar la semántica de movimientos, que puede mejorar considerablemente el rendimiento de las aplicaciones.Para obtener más información acerca de la semántica de movimientos, consulte Declarador de referencia Rvalue: &&.

En este tema se basa en la siguiente clase de C++, MemoryBlock, que administra un búfer de memoria.

// 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 != NULL)
      {
         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 desplazamiento para el ejemplo de clase de C++.

Para crear un constructor de movimiento para una clase de C++

  1. Definir un método de constructor vacío que toma una referencia valor r para el tipo de clase como parámetro, tal como se muestra en el ejemplo siguiente:

    MemoryBlock(MemoryBlock&& other)
       : _data(NULL)
       , _length(0)
    {
    }
    
  2. En el constructor de movimiento, asignar a los miembros de datos de la clase del objeto de origen para el objeto que se está construyendo:

    _data = other._data;
    _length = other._length;
    
  3. Asignar a los miembros de datos del objeto de origen a los valores predeterminados.Esto impide que el destructor de liberación de recursos (como la memoria) varias veces:

    other._data = NULL;
    other._length = 0;
    

Para crear un operador de asignación de movimiento para una clase de C++

  1. Definir un operador de asignación vacía que toma una referencia valor r para el tipo de clase como parámetro y devuelve una referencia al tipo de clase, como se muestra en el ejemplo siguiente:

    MemoryBlock& operator=(MemoryBlock&& other)
    {
    }
    
  2. En el operador de asignación y mover, agregar una instrucción condicional que no realiza ninguna operación si se intenta asignar el objeto a sí mismo.

    if (this != &other)
    {
    }
    
  3. En la instrucción condicional, liberar los recursos (como la memoria) desde el objeto que se va a asignar a.

    En el ejemplo siguiente, se libera el _data miembro del objeto al que se va a asignar para:

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

    Siga los pasos 2 y 3 en el primer procedimiento para transferir a los miembros de datos desde el objeto de origen para el objeto que se está construyendo:

    // 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 = NULL;
    other._length = 0;
    
  4. Devolver una referencia al objeto actual, tal como se muestra en el ejemplo siguiente:

    return *this;
    

Ejemplo

En el ejemplo siguiente se muestra la completa mover constructor y operador de asignación para el MemoryBlock clase:

// Move constructor.
MemoryBlock(MemoryBlock&& other)
   : _data(NULL)
   , _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 = NULL;
   other._length = 0;
}

// Move assignment operator.
MemoryBlock& operator=(MemoryBlock&& other)
{
   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 = NULL;
      other._length = 0;
   }
   return *this;
}

En el ejemplo siguiente se muestra cómo la semántica de movimientos puede mejorar el rendimiento de las aplicaciones.El ejemplo agrega dos elementos a un objeto vectorial y, a continuación, inserta un nuevo elemento entre los dos elementos existentes.En Visual C++ 2010, el vector usos de la clase mover semántica para realizar la operación de inserción eficazmente al mover los elementos del vector en lugar de copiarlos.

// 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.

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 = 25. Moving resource.
In ~MemoryBlock(). length = 0.
In MemoryBlock(MemoryBlock&&). length = 75. Moving resource.
In ~MemoryBlock(). length = 0.
In MemoryBlock(size_t). length = 50.
In MemoryBlock(MemoryBlock&&). length = 50. Moving resource.
In MemoryBlock(MemoryBlock&&). length = 50. Moving resource.
In operator=(MemoryBlock&&). length = 75.
In operator=(MemoryBlock&&). length = 50.
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 C++ 2010, en este ejemplo genera el siguiente resultado:

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 utiliza mover semántica es más eficaz que la versión que no utiliza la semántica de movimientos porque realiza menos copia, asignación de memoria y las operaciones de desasignación de memoria.

Programación eficaz

Para evitar pérdidas de recursos, libere siempre los recursos (como, por ejemplo, memoria, identificadores de archivo y sockets) en el operador de asignación de desplazamiento.

Para evitar que la destrucción irrecuperable de los recursos, controle adecuadamente los self-assignment en el operador de asignación de desplazamiento.

Si proporciona un constructor de movimiento y un operador de asignación de desplazamiento para su clase, puede eliminar código redundante escribiendo el constructor de movimiento para llamar al operador de asignación de desplazamiento.En el ejemplo siguiente se muestra una versión revisada del constructor de movimiento que llama el operador de asignación de movimiento:

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

El std::move función conserva la propiedad de valor r de la other parámetro.

Vea también

Referencia

Declarador de referencia Rvalue: &&

Otros recursos

<utility> move