C++11——移动语义

来源:互联网 发布:剑网三 捏脸数据 男 编辑:程序博客网 时间:2024/05/29 18:04

一般我们传递的右值都是临时变量,所以可以随意修改、如果我们知道函数的某个参数是一个右值,就可以将其作为一个临时存储或获取其中的内容,而不会影响程序的正确性。这也就意味着,比起拷贝右值参数的内容,再使用,不如直接使用该右值内容。当动态数组比较大的时候,这样可以节省很多内存分配,提供更多的优化空间。试想,一个函数以vector作为一个参数,就需要将其拷贝进来,而不对原始数据做任何操作。在C++03/98中,我们会采用如下的方法,将这个参数作为一个左值的const引用传入,然后做内存拷贝:

void copy(vector<int> const& vec){    vector<int> v(vec);    v.push_back(1);}

1这样就可以将左值或右值传递给该函数了,不过任何情况都是通过拷贝来完成的。如果使用右值引用版本的函数来重载这个函数,这就能避免在传入右值的时候,函数会进行内部拷贝的过程,因为可以任意的对原始值进行修改:

void copy(vector<int> const & vec){    vector<int> v(vec);    v.push_back(1);}void copy(vector<int> && vec){    vec.push_back(1);}

如果这个问题存在于类的构造函数中,获取内部右值在新的实例中使用。例如(默认构造函数会分配很大一块内存,在析构函数中释放)。

class X{private:    int* data;public:    X():        data(new int[1000000])    {}    ~X()    {        delete [] data;    }    X(const X& other):  // 1        data(new int[1000000])    {        std::copy(other.data,other.data+1000000,data);    }    //移动构造函数    X(X&& other):  // 2        data(other.data)    {        other.data=nullptr;    }};

一般拷贝构造函数的定义和上面的1一样:分配一块新内存,然后将原来的数据拷贝进来。不过现在有了一个新的构造函数,可以接收右值引用来获取老数据,这就是移动构造函数。在这个例子中,只是将指针拷贝到数据中,并将other以空指针的形式留在了新实例(data)中;使用右值来创建变量,就能避免空间和时间上的多余消耗。
1在有的情况下,有些类型的构造函数只支持移动构造函数,而不支持拷贝构造函数。例如智能指针std::unique_ptr<>的非空实例中,只允许这个指针指向其对象,所以函数在这里就不能用了(如果使用拷贝函数,就会有两个std::unique_ptr<>指向该对象,不满足std::unique_ptr<>定义)。不过,移动构造函数允许对指针的所有权,在实例之间进行传递,并且允许std::unique_ptr<>像一个带有返回值的函数一样使用——指针的转移是通过移动,而非拷贝。
如果你已经知道,某个变量在之后不会再用到了,这个时候你可以显示的移动,通过static_cast

X x1;X x2 = std::move(x1);X x3 = static_cast<X&&>(x2);

想要将参数值不通过拷贝,转化为本地变量或成员变量时,就可以使用这个办法;虽然右值引用参数绑定了右值,不过在函数内部,会当做左值来进行处理:

void do_stuff(X&& x_){    X a(x_);   //拷贝    X b(std::move(x_));  //移动}do_stuff(X());   // yes,右值绑定到右值引用上X x;do_stuff(x);  // no,左值不能绑定到右值引用上
0 0
原创粉丝点击