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