在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 constructor.
IntArray(IntArray&& other)
: _element(nullptr)
, _length(0)
{
*this = std::move(other);
}