《C++ Primer》读书笔记第十三章-3-对象移动

来源:互联网 发布:怎么申请做淘宝客服 编辑:程序博客网 时间:2024/06/05 04:08

笔记会持续更新,有错误的地方欢迎指正,谢谢!

对象移动

移动对象的优势:

  1. 我们在赋值操作时,对象拷贝后就立即销毁,但移动而非拷贝对象会大幅度提升性能。
  2. 有些类型是不能被拷贝的,如IO类和unique_ptr,在旧标准中我们无法在容器中保存它们,因为它们无法被拷贝,就不存在赋值之类的操作。但引入了移动操作后,我们就可以用容器保存它们。

右值引用

通过&&来获得右值引用,右值引用只能绑定到一个即将销毁的对象上,所以,我们才能自由地将一个右值引用的资源移动到另一个对象中。

记住左值长期存在的;右值是临时的,是即将销毁的。也就是左值持久,右值短暂
右值要么是字面常量,要么是求值过程中创建的临时对象。
来看几个例子:

int i = 42;int &r = i; //对int &&rr = i; //错,右值不是字面常量,也不是临时对象int &r2 = i * 24; //错,不能把临时对象赋给普通引用const int &r3 = i * 13; //对,可以把临时对象赋给引用常量int &&rr2 = i * 2; //对,右值是临时对象int &&rr3 = 42; //对,右值是字面值常量int &&rr4 = rr3; //错,绑定右值引用的变量rr3仍是左值,即rr3是正常的变量。

右值引用只能绑定到临时对象的原因:

  1. 所引用的对象将要被销毁
  2. 该对象没有用户

所以,使用右值引用的代码可以自由地接管所引用的对象的资源。

标准库move函数

强行右值,move算是一个移动构造函数:

int a = 12;int &&b = std::move(a) //move函数告诉编译器,要把这个左值a当右值处理。

调用move就意味着:除了对a赋值或销毁外,我们将不再使用它,例如我们不能把它的值赋给别人。

移动构造函数

类似对应的拷贝操作,但它从给定对象窃取资源而不是拷贝资源。
移动构造函数和拷贝构造函数的唯一区别就是它的引用是右值引用。
一旦资源完成移动,原对象必须不再指向被移动的资源,所有权已归属新对象。

用老朋友StrVec定义移动构造函数(它没有分配新内存):

StrVec::StrVec(StrVec &&s) noexcept : elements(s.elements),first_free(s.first_free), cap(s.cap) //noexcept表示不抛出异常//noexcept:声明和定义不抛出异常的移动构造函数和移动赋值函数都要显式//指定noexcept,否则系统会使用拷贝操作。{    //上面的列表初始化就移动好了,注意第一个参数是非const右值引用    //接下来的话保证s进入这样的状态-对其进行析构函数是安全的    s.elements = s.first_free = s.cap = nullptr;}

其实就是s把资源给了新对象,自己都变成空指针,深藏功与名了。

移动赋值运算符

类似对应的拷贝操作,但它从给定对象窃取资源而不是拷贝资源。

和赋值运算符类似,也要正确处理自赋值情况:

StrVec &StrVec::operator=(StrVec &&rhs) noexcept{    if(this != &rhs) //检测,不是自赋值再进行下面步骤,是自赋值直接返回    {        free(); //因为它要接管rhs,原来的内存就不用了。        //从rhs窃取资源        elements = rhs.elements;        first_free = rhs.first_free;        cap = rhs.cap;        //将rhs置于可析构状态        rhs.elements = rhs.first_free = rhs.cap = nullptr;    }    return *this;}

移后原对象要保持有效的,可析构的状态,但最好不要去动它,除了析构它之外,让它安静地功成身退。

合成的移动操作

没有任何自定义的拷贝控制成员,且每个非stastic数据成员都可以移动时,编译器才会为他们合成移动构造函数或移动赋值运算符。

移动和拷贝

编译器使用普通的函数匹配规则确定使用哪个拷贝控制成员。通常,拷贝左值,移动右值。如果没有移动构造函数,右值也被拷贝。

移动迭代器

通过标准库的make_move_iterator函数来将普通迭代器转换为移动迭代器。

原创粉丝点击