Share via


Comment : Entrez un constructeur de mouvements

Cette rubrique explique comment écrire un déplacer constructeur et un opérateur d'assignation de déplacement pour une classe C++.Un constructeur de déplacement vous permet d'implémenter une sémantique de déplacement, qui peut améliorer considérablement les performances de vos applications.Pour plus d'informations sur la sémantique de déplacement, voir Déclarateur de référence Rvalue : &&.

Cette rubrique s'appuie sur la classe C++ suivante, MemoryBlock, qui gère une mémoire tampon.

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

Les procédures suivantes décrivent comment écrire un constructeur de déplacement et un opérateur d'assignation de déplacement pour l'exemple de classe C++.

Pour créer un constructeur de déplacement d'une classe C++

  1. Définir une méthode de constructeur vide qui prend une valeur rvalue référence au type de classe comme son paramètre, comme illustré dans l'exemple suivant :

    MemoryBlock(MemoryBlock&& other)
       : _data(NULL)
       , _length(0)
    {
    }
    
  2. Dans le constructeur de déplacement, affecter des membres de données de la classe à partir de l'objet source pour l'objet en cours de construction :

    _data = other._data;
    _length = other._length;
    
  3. Affecter les membres de l'objet source de données pour les valeurs par défaut.Cela empêche le destructeur de libération des ressources (telles que la mémoire) plusieurs fois :

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

Pour créer un opérateur d'assignation de déplacement pour une classe C++

  1. Définir un opérateur d'assignation vide qui prend une valeur rvalue référence au type de classe comme son paramètre et retourne une référence au type de classe, comme indiqué dans l'exemple suivant :

    MemoryBlock& operator=(MemoryBlock&& other)
    {
    }
    
  2. Dans l'opérateur d'assignation de déplacement, ajoutez une instruction conditionnelle qui exécute aucune opération si vous tentez d'assigner l'objet à lui-même.

    if (this != &other)
    {
    }
    
  3. Dans l'instruction conditionnelle, libérer toutes les ressources (telles que la mémoire) à partir de l'objet qui est assignée à.

    L'exemple suivant libère le _data membre de l'objet qui est assignée à :

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

    Suivez les étapes 2 et 3 dans la première procédure pour transférer les données membres de l'objet source vers l'objet qui est construite :

    // 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. Renvoyer une référence à l'objet en cours, comme illustré dans l'exemple suivant :

    return *this;
    

Exemple

L'exemple suivant illustre le constructeur et déplacent opérateur d'assignation pour la MemoryBlock classe :

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

L'exemple suivant montre comment une sémantique de déplacement peut améliorer les performances de vos applications.L'exemple ajoute deux éléments à un objet vectoriel, puis insère un nouvel élément entre les deux éléments existants.Dans Visual C++ 2010, la vector classe utilise déplacer sémantique pour effectuer l'opération d'insertion efficacement en déplaçant les éléments du vecteur au lieu de les copier.

// 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));
}

Cet exemple produit la sortie suivante :

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.

Avant de Visual C++ 2010, cet exemple produit la sortie suivante :

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 version de cet exemple que les utilisations déplacement sémantique est plus efficace que la version qui n'utilise pas de sémantique de déplacement car elle effectue moins copie, l'allocation de mémoire et opérations de désallocation de mémoire.

Programmation fiable

Pour empêcher les fuites de ressource, vous devez toujours libérer ressources (telles que la mémoire, les handles de fichiers et sockets) de l'opérateur d'assignation de déplacement.

Pour éviter la destruction des ressources non récupérable, gérer correctement les self-assignment dans l'opérateur d'assignation de déplacement.

Si vous fournissez un constructeur de déplacement et un opérateur d'assignation de déplacement pour votre classe, vous pouvez éliminer le code redondant en écrivant le constructeur de déplacement pour appeler l'opérateur d'assignation de déplacement.L'exemple suivant montre une version révisée du constructeur qui appelle l'opérateur d'assignation de déplacement déplacement :

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

Le std::move fonction conserve la propriété valeur rvalue de la other paramètre.

Voir aussi

Référence

Déclarateur de référence Rvalue : &&

Autres ressources

<utility> move