左值与右值
在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。临时变量如参数传入到函数内部创建的临时变量以及函数返回创建的临时变量都为右值,常量也为右值如 “中国”。
左值引用与右值引用
引用就是一个对象的别名,左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。右值引用和左值引用都是属于引用类型,并且都是左值。
不能将一个右值引用直接绑定到左值上。如下代码是错误的。
int i=40;
int && j=i;
但是可以通过显示的类型转换获得指向一个左值的右值引用,通常用std::move()来获得指向左值的右值引用。
std::move()
std::move的定义:
这里,T&&是通用引用,需要注意和右值引用(比如int&&)区分。通过move定义可以看出,move并没有”移动“什么内容,只是将传入的值转换为右值 ,此外没有其他动作,只是返回指向参数对象的右值引用,参数对象没有丝毫变化,其对资源依旧保有主导权。std::move+移动构造函数或者移动赋值运算符,才能充分起到减少不必要拷贝的意义,以及完成真正的资源转移,掏空move参数对象。
调用std::move() 往往伴随着移动构造和移动赋值,所以被 move过的对象需要保证不在被访问(可以被销毁和赋值,但不能使用对象值),这意味着即将被销毁的对象才适合被move()。
移动构造函数和移动赋值函数
普通构造函数和赋值函数代码如下:
node(const node& otherNode) {
size = otherNode.size;
ptr = new int[size];
for (int i = 0; i < size; i++) {
ptr[i] = otherNode.ptr[i];
}
}
node& operator=(const node& otherNode) {
size = otherNode.size;
ptr = new int[size];
for (int i = 0; i < size; i++) {
ptr[i] = otherNode.ptr[i];
}
}
移动构造函数和移动赋值函数示例代码如下:
node(node&& otherNode) {
size = otherNode.size;
ptr = otherNode.ptr;
otherNode.ptr=nullptr;
}
node& operator=(node&& otherNode) {
size = otherNode.size;
ptr = otherNode.ptr;
otherNode.ptr=nullptr;
}
以移动构造函数为例,参数传入的时候是右值引用,所以不会创造临时变量,这和左值引用是一样的(此处是第一个提高效率的地方,不创造临时变量),函数内代码只是简单拿来右值引用 对各个变量赋值,没有申请内存,再copy的操作(此处是第二个提高效率的地方,也是引入右值引用的最终目的。)对于指针成员这么做通常情况下绝对不可以的,但是如果是右值引用完全可以拿来主义,因为右值引用的对象生命已经接近尾声,为了提高效率直接将其管理的堆上内存拿来,省去了申请内存 在拷贝的操作大大提高效率。需要注意的是右值引用的指针成员必须赋值为空指针,否则右值引用指向的对象在析构的时候会析构掉被转移的堆上内存。赋值为空指针才是源对象放弃了对资源的管理,真正的完成了资源的转移。