左值:有地址有名称的变量叫做左值
右值:字面常量或者创建的临时对象
左值引用:常见的我们使用的&a就是左值引用,必须绑定到左值中
右值引用:为了支持移动操作引用的一种类型,通过&&获得,必须绑定到右值中,将被绑定的对象的资源“移动”到另一个对象中(指向要被销毁的对象)
int &&a=3;//正确
int &&b=a;//错误,表达式a是左值
例如在StrVec中使用移动操作更高效(vector)
void StrVec::reallocate()
{
auto newcapacity=size()?2*size()+1;
auto newdata=allo.allocate(newcapacity);
auto dest=newdat;//指向新数组
auto elem=elements;//指向旧数组
for(size_t i=0;i!=size();i++)
alloc.construct(dest++,std::move(*elem++));//这里使用移动操作,将旧数组中string管理的内存空间所有权给新数组(有点类似auto_ptr转让所有权+浅拷贝)
free();//释放旧的内存空间
//更新我们的数据结构,执行新元素
elements=newdata;
first_free=dest;//指向最后一个构造元素之后的位置
cap=elemnets+newcapacity;//指向新分配空间的尾后位置
}
move函数:可以显式的将左值转换为对应的右值引用类型
int &&b=std::move(a);//ok
move调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它.调用move意味着:除了对a赋值或销毁它,我们将不再使用它,在调用move后,我们不能对移后源对象的值做任何的假设(不能使用一个移后源对象的值).
移动构造函数和移动赋值运算符
保证销毁移后源对象是无害的,一旦资源完成移动,源对象必须不再指向被移动的资源
StrVec(StrVec &&s) noexcept
:elements(s.elements),first_free(s.first_free),cap(s.cap)
{
//令s进入这样的状态——对其运行析构函数是安全的
s.elements=s.first_free=s.cap=nullptr;
}