Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu konuda, bir C++ sınıfı için taşıma oluşturucu ve taşıma atama işlecinin nasıl yazıldığı açıklanmaktadır. Taşıma oluşturucu, bir rvalue nesnesinin sahip olduğu kaynakların kopyalama olmadan bir lvalue'ya taşınmasını sağlar. Taşıma semantiği hakkında daha fazla bilgi için bkz . Rvalue Başvuru Bildirimcisi: &.
Bu konu, MemoryBlockbir bellek arabelleği yöneten aşağıdaki C++ sınıfına dayandırılmaktadır.
// 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.
};
Aşağıdaki yordamlarda, örnek C++ sınıfı için taşıma oluşturucu ve taşıma atama işlecinin nasıl yazıldığı açıklanmaktadır.
C++ sınıfı için taşıma oluşturucu oluşturmak için
Aşağıdaki örnekte gösterildiği gibi sınıf türüne parametresi olarak rvalue başvurusu alan boş bir oluşturucu yöntemi tanımlayın:
MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { }Taşıma oluşturucusunda, kaynak nesneden sınıf veri üyelerini oluşturulan nesneye atayın:
_data = other._data; _length = other._length;Kaynak nesnenin veri üyelerini varsayılan değerlere atayın. Bu, yıkıcının kaynakları (bellek gibi) birden çok kez boşaltmasını önler:
other._data = nullptr; other._length = 0;
C++ sınıfı için taşıma atama işleci oluşturmak için
Aşağıdaki örnekte gösterildiği gibi, sınıf türüne bir rvalue başvurusunu parametresi olarak alan ve sınıf türüne bir başvuru döndüren boş bir atama işleci tanımlayın:
MemoryBlock& operator=(MemoryBlock&& other) { }Atamayı taşıma işlecinde, nesneyi kendisine atamayı denerseniz hiçbir işlem gerçekleştirmeyan bir koşullu deyim ekleyin.
if (this != &other) { }Koşullu deyimde, atanmakta olan nesneden tüm kaynakları (bellek gibi) serbest bırakın.
Aşağıdaki örnek, üyeyi
_dataatanmakta olan nesneden serbesttir:// Free the existing resource. delete[] _data;Veri üyelerini kaynak nesneden oluşturulan nesneye aktarmak için ilk yordamdaki 2. ve 3. adımları izleyin:
// 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;Aşağıdaki örnekte gösterildiği gibi geçerli nesneye bir başvuru döndürür:
return *this;
Örnek: Tam taşıma oluşturucu ve atama işleci
Aşağıdaki örnek, sınıfın tam taşıma oluşturucu ve taşıma atama işlecini MemoryBlock gösterir:
// 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;
}
Örnek Performansı geliştirmek için taşıma semantiğini kullanma
Aşağıdaki örnekte, taşıma semantiğinin uygulamalarınızın performansını nasıl artırabileceği gösterilmektedir. Örnek, bir vektör nesnesine iki öğe ekler ve ardından mevcut iki öğe arasına yeni bir öğe ekler. sınıfı, vector ekleme işlemini verimli bir şekilde gerçekleştirmek için vektör öğelerini kopyalamak yerine taşıyarak taşıma semantiğini kullanır.
// 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));
}
Bu örnek aşağıdaki çıkışı oluşturur:
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.
Visual Studio 2010'da bu örnek aşağıdaki çıkışı üretti:
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.
Bu örneğin taşıma semantiğini kullanan sürümü, daha az kopyalama, bellek ayırma ve bellek ayırma işlemi gerçekleştirdiğinden taşıma semantiğini kullanmayan sürümden daha verimlidir.
Güçlü Programlama
Kaynak sızıntılarını önlemek için taşıma atama işlecindeki kaynakları (bellek, dosya tanıtıcıları ve yuvalar gibi) her zaman boşaltın.
Kaynakların kurtarılamaz şekilde yok edilmesini önlemek için taşıma atama işlecinde kendi kendine atamayı düzgün bir şekilde işleyin.
Sınıfınız için hem taşıma oluşturucu hem de taşıma atama işleci sağlarsanız, taşıma atama işlecini çağırmak için taşıma oluşturucuyu yazarak yedekli kodu ortadan kaldırabilirsiniz. Aşağıdaki örnek, taşıma atama işlecini çağıran taşıma oluşturucusunun düzeltilmiş bir sürümünü gösterir:
// Move constructor.
MemoryBlock(MemoryBlock&& other) noexcept
: _data(nullptr)
, _length(0)
{
*this = std::move(other);
}
std::move işlevi lvalue değerini bir rvalue'ya other dönüştürür.