具體參考: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的解釋:
Move semantics (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。這個函數僅僅是把一個左值引用變爲一個右值引用。