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。这个函数仅仅是把一个左值引用变为一个右值引用。






你可能感兴趣的:(C++11新特性(五)看看外国佬写的代码move的使用)