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

 

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