浅析C++11中的右值引用、转移语义和完美转发

2020-01-06 15:44:07丽君

         非常量左值引用只能绑定到非常量左值;

         非常量右值引用只能绑定到非常量右值(vs2013也可以绑定到常量右值);

         常量右值引用只能绑定到常量和非常量右值(非常量右值引用只是为了语义的完整而存在,常量左值引用就可以实现它的作用).

         虽然从绑定规则中可以看出常量左值引用也可以绑定到右值,但显然不可以改变右值的值,右值引用就可以,从而实现转移语义,因为右值引用通常要改变所绑定的右值,所以被绑定的右值不能为const.

    注意:右值引用是左值!

3. 转移语义(move semantics):

    右值引用被引入的目的之一就是实现转移语义,转移语义可以将资源 ( 堆,系统对象等 ) 的所有权从一个对象(通常是匿名的临时对象)转移到另一个对象,从而减少对象构建及销毁操作,提高程序效率(这在2的例子中已经作了解释).转移语义与拷贝语义是相对的.从转移语义可以看出,实际上,转移语义并不是新的概念,它实际上已经在C++98/03的语言和库中被使用了,比如在某些情况下拷贝构造函数的省略(copy constructor elision in some contexts),智能指针的拷贝(auto_ptr “copy”),链表拼接(list::splice)和容器内的置换(swap on containers)等,只是还没有统一的语法和语义支持

    虽然普通的函数和操作符也可以利用右值引用实现转移语义(如2中的例子),但转移语义通常是通过转移构造函数和转移赋值操作符实现的.转移构造函数的原型为Classname(Typename&&) ,而拷贝构造函数的原型为Classname(const Typename&) ,转移构造函数不会被编译器自动生成,需要自己定义,只定义转移构造函数也不影响编译器生成拷贝构造函数,如果传递的参数是左值,就调用拷贝构造函数,反之,就调用转移构造函数.

例如:


class Demo{

public:

  Demo():p(new int[10000]{};

  Demo(Demo&& lre):arr(lre.arr),size(lra.size){lre.arr=NULL;}//转移构造函数

  Demo(const Demo& lre):arr(new int[10000]),size(arr.size){

    for(int cou=0;cou<10000;++cou)

      arr[cou]=lew.arr[cou];

  }

private:

  int size;

  int* arr;

}

    从以上代码可以看出,拷贝构造函数在堆中重新开辟了一个大小为10000的int型数组,然后每个元素分别拷贝,而转移构造函数则是直接接管参数的指针所指向的资源,效率搞下立判!需要注意的是转移构造函数实参必须是右值,一般是临时对象,如函数的返回值等,对于此类临时对象一般在当行代码之后就被销毁,而采用转移构造函数可以延长其生命期,可谓是物尽其用,同时有避免了重新开辟数组.对于上述代码中的转移构造函数,有必要详细分析一下: