Megosztás a következőn keresztül:


Konstruktorok áthelyezése és hozzárendelési operátorok áthelyezése (C++)

Ez a témakör azt ismerteti, hogyan írhat áthelyezési konstruktort és áthelyezés-hozzárendelési operátort egy C++ osztályhoz. Az áthelyezési konstruktorok lehetővé teszik, hogy az rvalue objektum tulajdonában lévő erőforrások másolás nélkül átkerüljenek egy lvalue-ba. Az áthelyezési szemantikával kapcsolatos további információkért lásd : Rvalue Reference Deklarátor: &>.

Ez a témakör a következő C++ osztályra épül, MemoryBlockamely egy memóriapuffert kezel.

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

Az alábbi eljárások bemutatják, hogyan írhat áthelyezési konstruktort és áthelyezés-hozzárendelési operátort a C++ példaosztályhoz.

Áthelyezési konstruktor létrehozása C++ osztályhoz

  1. Adjon meg egy üres konstruktormetódust, amely paraméterként az osztálytípusra mutató rvalue-hivatkozást vesz fel, ahogyan az az alábbi példában is látható:

    MemoryBlock(MemoryBlock&& other)
       : _data(nullptr)
       , _length(0)
    {
    }
    
  2. Az áthelyezési konstruktorban rendelje hozzá a forrásobjektum osztályadat-tagjait a létrehozandó objektumhoz:

    _data = other._data;
    _length = other._length;
    
  3. A forrásobjektum adattagjainak hozzárendelése az alapértelmezett értékekhez. Ez megakadályozza, hogy a destruktor többször felszabadítsa az erőforrásokat (például a memóriát):

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

Áthelyezési hozzárendelési operátor létrehozása C++ osztályhoz

  1. Adjon meg egy üres hozzárendelési operátort, amely paraméterként az osztálytípusra mutató rvalue-hivatkozást használ, és az osztálytípusra mutató hivatkozást ad vissza, ahogyan az az alábbi példában is látható:

    MemoryBlock& operator=(MemoryBlock&& other)
    {
    }
    
  2. Az áthelyezés-hozzárendelési operátorban adjon hozzá egy feltételes utasítást, amely nem hajt végre műveletet, ha az objektumot önmagához próbálja hozzárendelni.

    if (this != &other)
    {
    }
    
  3. A feltételes utasításban szabadít fel minden erőforrást (például memóriát) a hozzárendelt objektumból.

    Az alábbi példa felszabadítja a _data tagot a hozzárendelt objektumból:

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

    Kövesse az első eljárás 2. és 3. lépését az adattagok forrásobjektumból a létrehozandó objektumba való átviteléhez:

    // 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. Az aktuális objektumra mutató hivatkozás visszaadása az alábbi példában látható módon:

    return *this;
    

Példa: Teljes áthelyezési konstruktor és hozzárendelési operátor

Az alábbi példa az osztály teljes áthelyezési konstruktorát és áthelyezési hozzárendelési operátorát mutatja be MemoryBlock :

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

Példa: A teljesítmény javítása az áthelyezés szemantikájának használatával

Az alábbi példa bemutatja, hogyan javíthatja az alkalmazások teljesítményét az áthelyezés szemantikája. A példa két elemet ad hozzá egy vektorobjektumhoz, majd beszúr egy új elemet a két meglévő elem közé. Az vector osztály áthelyezési szemantikával hatékonyan hajtja végre a beszúrási műveletet a vektor elemeinek másolása helyett.

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

Ez a példa a következő kimenetet hozza létre:

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.

A Visual Studio 2010 előtt ez a példa a következő kimenetet hozta létre:

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.

A példa áthelyezési szemantikát használó verziója hatékonyabb, mint az áthelyezési szemantikát nem használó verzió, mivel kevesebb másolási, memóriafoglalási és memóriaelhelyezési műveletet hajt végre.

Robusztus programozás

Az erőforrásszivárgások elkerülése érdekében mindig szabadítsunk fel erőforrásokat (például memóriát, fájlfogópontokat és szoftvercsatornákat) az áthelyezés-hozzárendelési operátorban.

Az erőforrások helyreállíthatatlan megsemmisítésének megakadályozása érdekében megfelelően kezelje az önkiosztást az áthelyezés-hozzárendelési operátorban.

Ha egy áthelyezési konstruktort és egy áthelyezés-hozzárendelési operátort is megad az osztálynak, a redundáns kódot megszüntetheti az áthelyezési konstruktor megírásával, hogy meghívja az áthelyezési hozzárendelés-operátort. Az alábbi példa az áthelyezési konstruktor módosított verzióját mutatja be, amely meghívja az áthelyezés-hozzárendelési operátort:

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

Az std::move függvény az lvalue-t other rvalue-vá alakítja.

Lásd még

Rvalue Reference Deklarátor: &>
std::áthelyezés