C++11 自定义Move构造函数(Move Constructor)和Move赋值运算符(Move Assignment Operators)

在C++11的标准当中,引入了右值的概念以及跟右值密切相关的Move语义,由此C++的类定义中也多了2个成员函数:Move构造函数和Move赋值运算符。这篇文章将讨论如何为我们自己的类编写Move 构造函数Move赋值运算符

class IntArray 是一个Int 类型的数组,它的类定义如下:

#pragma once
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

class IntArray
{
public:
	//constructor
	explicit IntArray(size_t len)
		: _length(len)
		, _element(new int[len])
	{
		cout << "In Constructor: _length=" << len << endl;
	}
	IntArray()
		: _length(0)
		, _element(nullptr)
	{

	}
	// deconstructor
	~IntArray()
	{
		if (_element != nullptr)
		{
			cout << "Deconstructor - Delete element......";
			delete[] _element;
			_length = 0;
		}
		cout << endl;
	}
	// copy constructor
	IntArray(const IntArray& other)
		:_length(other._length)
		, _element(new int[other.length()])
	{
		cout << "In Copy Constructor!!" << endl;
		memcpy(_element, other._element, _length);

	}
	// copy assignment operator
	IntArray& operator=(const IntArray& other)
	{
		cout << "In Copy Assignment operator!" << endl;
		if (this != &other)
		{
			delete[] _element;
			_length = other._length;
			_element = new int[_length];
			memcpy(_element, other._element, _length);
		}
		return *this;
	}

	size_t length() const
	{
		return _length;
	}
	int * element() const
	{
		return _element;
	}

public:
	size_t _length;
	int * _element;
};

定义Move构造函数(Move Constructor)

下面我们按如下步骤为他定义一个 move 构造函数

Step1 定义一个空的构造函数体,构造函数接受一个IntArray类型的右值引用作为参数。

IntArray(IntArray&& other)
		: _length(0)
		, _element(nullptr)
	{
            // an Empty move Constructor body,
            //it takes an rvlaue to this class type as its parameter
            
        }

Step 2 将源对象的数据成员的值一一对应地赋值给目的对象。

_length = other._length;
_element = other._element;

Step 3 将源对象的数据数据成员清空,这样做的目的是组织源对象的析构函数将其掌握的资源析构。

other._length = 0;
other._element = nullptr;

这样,IntArray的Move构造函数就写好啦

IntArray(IntArray&& other)
		: _length(0)
		, _element(nullptr)
	{
		cout << "In move Constructor IntArray(IntArray&& other)..." << endl;
		_length = other._length;
		_element = other._element;

		other._length = 0;
		other._element = nullptr;
	}

定义Move 赋值运算符(Move Assignment Operators)

下面我们再来看看,如何定义Move赋值运算符

Step 1 定义一个空的赋值运算符的函数体,同样接受一个IntArray的右值引用作为其参数

IntArray& operator=(IntArray&& other)
{
      // take an rvalue to IntArray as its parameter 
}

Step 2 在定义赋值运算符的时候,有一件很重要的事儿就是防止自我赋值

if (this != &other)
{
    //TO DO .....

}

Step 3 做完针对自我赋值的检测后,让我们先删除目标对象自身的资源

if (!_element)
     delete[] _element;

Step 4 接下来我们要做的工作与定义Move 构造函数时做的事情相同,即将源对象的数据成员原封不动的赋值给目标对象的数据成员,然后清空源对象的数据成员值。

// set value from other to this
_element = other._element;
_length = other._length;

// clear other's data member value
other._length = 0;
other._element = nullptr;

Step 5 最后,不要忘了返回目标对象

return *this;

完整的Move赋值函数如下:

IntArray& operator=(IntArray&& other)
{
	cout << "In move Copy Assignment IntArray& operator=(IntArray&& other)" << endl;
	if (this != &other)
	{
		if (!_element)
			delete[] _element;
		_element = other._element;
		_length = other._length;

		other._length = 0;
		other._element = nullptr;

	}
	return *this;
}

最后,我们需要注意的地方是,当我们在定义Move赋值运算符的时候,切记要把源对象的资源释放掉,这些资源包括内存,文件句柄,Socket等等。因为需要释放资源的操作,所以在释放之前一定要防止自我赋值,一定要做自我赋值的检测。

如果我们为自己的类定义了Move拷贝函数和Move赋值函数,我们还可以以更简洁的方法来实现Move构造函数:

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章