C++11新特性(五)看看外國佬寫的代碼move的使用

具體參考: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 != &copy)

        {

           _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。這個函數僅僅是把一個左值引用變爲一個右值引用。






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