具体参考:http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer
最后部分,为了理解move的使用,copy下代码调试下:
外国人写的代码就是简洁,一目了然。
#include <iostream>
#include <assert.h>
template <typename T>
class Buffer
{
std::string _name;
size_t _size;
std::unique_ptr<T[]> _buffer;
public:
// default constructor
Buffer():
_size(16),
_buffer(new T[16])
{}
// constructor
Buffer(conststd::string& name,size_t size):
_name(name),
_size(size),
_buffer(new T[size])
{}
// copy constructor
Buffer(constBuffer& copy):
_name(copy._name),
_size(copy._size),
_buffer(new T[copy._size])
{
T* source = copy._buffer.get();
T* dest =_buffer.get();
std::copy(source, source + copy._size, dest);
}
// copy assignment operator
Buffer& operator=(constBuffer& copy)
{
if(this != ©)
{
_name = copy._name;
if(_size != copy._size)
{
_buffer = nullptr;
_size = copy._size;
_buffer = _size >0 ? new T[_size] :nullptr;
}
T* source = copy._buffer.get();
T* dest =_buffer.get();
std::copy(source, source + copy._size, dest);
}
return *this;
}
// move constructor
Buffer(Buffer&& temp):
_name(std::move(temp._name)),
_size(temp._size),
_buffer(std::move(temp._buffer))
{
temp._buffer =nullptr;
temp._size =0;
}
// move assignment operator
Buffer& operator=(Buffer&& temp)
{
assert(this != &temp);// assert if this is not a temporary
_buffer = nullptr;
_size = temp._size;
_buffer = std::move(temp._buffer);
_name = std::move(temp._name);
temp._buffer =nullptr;
temp._size =0;
return *this;
}
};
template <typename T>
Buffer<T> getBuffer(conststd::string& name)
{
Buffer<T> b(name, 128);
return b;
}
int main()
{
Buffer<int> b1;
Buffer<int> b2("buf2",64);
Buffer<int> b3 = b2;
Buffer<int> b4 =getBuffer<int>("buf4");
b1 =getBuffer<int>("buf5");
return 0;
}
看看外国对move的解释:
这是C++11中所涵盖的另一个重要话题。就这个话题可以写出一系列文章,仅用一个段落来说明显然是不够的。因此在这里我不会过多的深入细节,如果你还不是很熟悉这个话题,我鼓励你去阅读更多地资料。
C++11加入了右值引用(rvalue reference)的概念(用&&标识),用来区分对左值和右值的引用。左值就是一个有名字的对象,而右值则是一个无名对象(临时对象)。move语义允许修改右值(以前右值被看作是不可修改的,等同于const T&类型)。
C++的class或者struct以前都有一些隐含的成员函数:默认构造函数(仅当没有显示定义任何其他构造函数时才存在),拷贝构造函数,析构函数还有拷贝赋值操作符。拷贝构造函数和拷贝赋值操作符提供bit-wise的拷贝(浅拷贝),也就是逐个bit拷贝对象。也就是说,如果你有一个类包含指向其他对象的指针,拷贝时只会拷贝指针的值而不会管指向的对象。在某些情况下这种做法是没问题的,但在很多情况下,实际上你需要的是深拷贝,也就是说你希望拷贝指针所指向的对象。而不是拷贝指针的值。这种情况下,你需要显示地提供拷贝构造函数与拷贝赋值操作符来进行深拷贝。
如果你用来初始化或拷贝的源对象是个右值(临时对象)会怎么样呢?你仍然需要拷贝它的值,但随后很快右值就会被释放。这意味着产生了额外的操作开销,包括原本并不需要的空间分配以及内存拷贝。
现在说说move constructor和move assignment operator。这两个函数接收T&&类型的参数,也就是一个右值。在这种情况下,它们可以修改右值对象,例如“偷走”它们内部指针所指向的对象。举个例子,一个容器的实现(例如vector或者queue)可能包含一个指向元素数组的指针。当用一个临时对象初始化一个对象时,我们不需要分配另一个数组,从临时对象中把值复制过来,然后在临时对象析构时释放它的内存。我们只需要将指向数组内存的指针值复制过来,由此节约了一次内存分配,一次元数组的复制以及后来的内存释放。
以上代码实现了一个简易的buffer。这个buffer有一个成员记录buffer名称(为了便于以下的说明),一个指针(封装在unique_ptr中)指向元素为T类型的数组,还有一个记录数组长度的变量。
默认的copy constructor以及copy assignment operator大家应该很熟悉了。C++11中新增的是move constructor以及move assignment operator,这两个函数根据上文所描述的move语义实现。如果你运行这段代码,你就会发现b4构造时,move constructor会被调用。同样,对b1赋值时,move assignment operator会被调用。原因就在于getBuffer()的返回值是一个临时对象——也就是右值。
你也许注意到了,move constuctor中当我们初始化变量name和指向buffer的指针时,我们使用了std::move。name实际上是一个string,std::string实现了move语义。std::unique_ptr也一样。但是如果我们写_name(temp._name),那么copy constructor将会被调用。不过对于_buffer来说不能这么写,因为std::unique_ptr没有copy constructor。但为什么std::string的move constructor此时没有被调到呢?这是因为虽然我们使用一个右值调用了Buffer的move constructor,但在这个构造函数内,它实际上是个左值。为什么?因为它是有名字的——“temp”。一个有名字的对象就是左值。为了再把它变为右值(以便调用move constructor)必须使用std::move。这个函数仅仅是把一个左值引用变为一个右值引用。