Gewusst wie: Schreiben Sie einen Verschiebungskonstruktor

Dieses Thema beschreibt, wie schreibe ich eine move-Konstruktor und einen Zuweisungsoperator verschieben für eine C++-Klasse.Ein Konstruktor verschieben können Sie verschieben Semantik zu implementieren, die Leistung Ihrer Anwendungen deutlich steigern kann.Weitere Informationen zum Verschieben-Semantik finden Sie unter Rvalu-Verweis-Deklarator: &&.

In diesem Thema stützt sich auf die folgenden C++-Klasse, MemoryBlock, die verwaltet einen Arbeitsspeicherpuffer.

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

Im folgenden wird beschrieben, wie Sie einen Konstruktor verschieben und einen Zuweisungsoperator verschieben, beispielsweise C++-Klasse zu schreiben.

Erstellen Sie einen Konstruktor verschieben für eine C++-Klasse

  1. Definieren Sie eine leeren Konstruktor-Methode, die einen Verweis auf den Klassentyp als Parameter annimmt, wie im folgenden Beispiel gezeigt:

    MemoryBlock(MemoryBlock&& other)
       : _data(NULL)
       , _length(0)
    {
    }
    
  2. Weisen Sie die Datenmember der Klasse aus dem Quellobjekt auf das Objekt, das erstellt wird, in den Konstruktor verschieben:

    _data = other._data;
    _length = other._length;
    
  3. Weisen Sie die Datenmember des Quellobjekts auf Standardwerte.Dadurch wird verhindert, dass den Destruktor Freigeben von Ressourcen (z. B. Arbeitsspeicher) mehrere Male:

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

Um einen Zuweisungsoperator verschieben für eine C++-Klasse zu erstellen.

  1. Definieren Sie einen leeren Zuweisungsoperator, der einen Verweis auf den Typ als Parameter und gibt einen Verweis auf den Klassentyp, wie im folgenden Beispiel gezeigt:

    MemoryBlock& operator=(MemoryBlock&& other)
    {
    }
    
  2. In der Zuweisungsoperator verschieben Hinzufügen einer bedingten Anweisung, die keine Operation ausführt, wenn Sie versuchen, das Objekt selbst zuweisen.

    if (this != &other)
    {
    }
    
  3. Befreien Sie in der bedingten Anweisung alle Ressourcen (z. B. Arbeitsspeicher) von das Objekt, das zugeordnet wird.

    Das folgende Beispiel gibt die _data Element aus dem Objekt, das zugeordnet wird:

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

    Führen Sie die Schritte 2 und 3 in der ersten Prozedur die Datenelemente aus dem Quellobjekt auf das Objekt zu übertragen, die erstellt wird:

    // 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. Geben Sie einen Verweis auf das aktuelle Objekt zurück, wie im folgenden Beispiel gezeigt:

    return *this;
    

Beispiel

Das folgende Beispiel zeigt die vollständige Konstruktor verschieben und Verschieben der Zuweisungsoperator für die MemoryBlock Klasse:

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

Das folgende Beispiel zeigt, wie verschieben Semantik die Leistung Ihrer Anwendungen verbessern können.Im Beispiel ein Vector-Objekt zwei Elemente hinzugefügt und dann ein neues Element zwischen den beiden vorhandenen Elementen eingefügt.In Visual C++ 2010, die vector -Klasse verwendet verschieben Semantik der Einfügevorgang effizient durchführen, durch Verschieben der Elemente eines Vektors, anstatt sie zu kopieren.

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

Dieses Beispiel erzeugt folgende Ausgabe:

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.

Vor Visual C++ 2010, in diesem Beispiel erzeugt die folgende Ausgabe:

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.

Die Version dieses Beispiels verwendet die Semantik verschieben ist effizienter als die Version, die nicht verschieben Semantik verwendet, da sie weniger kopieren, Speicherreservierung und Speichervorgänge Deallocation ausführt.

Robuste Programmierung

Um Ressourcenverluste zu verhindern, geben Sie Ressourcen (z. B. Arbeitsspeicher, Dateihandles und Sockets) in der Zuweisungsoperator verschieben immer frei.

Um nicht behebbare Zerstörung von Ressourcen zu verhindern, behandeln Sie richtig Self-assignment in der Zuweisungsoperator verschieben.

Wenn Sie einen Konstruktor verschieben und einen Zuweisungsoperator verschieben für die Klasse bereitzustellen, eliminieren Sie redundanten Code, durch den Konstruktor verschieben, um den Zuweisungsoperator verschieben aufrufen schreiben.Das folgende Beispiel zeigt eine überarbeitete Version des Konstruktors verschieben, der den Zuweisungsoperator verschieben aufruft:

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

Die std::move Funktion behält die r-Wert-Eigenschaft, der die other Parameter.

Siehe auch

Referenz

Rvalu-Verweis-Deklarator: &&

Weitere Ressourcen

<utility> move