您的位置:首页 > 编程语言 > C语言/C++

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

2017-12-23 22:52 253 查看
笔记会持续更新,有错误的地方欢迎指正,谢谢!

对象移动

移动对象的优势:

我们在赋值操作时,对象拷贝后就立即销毁,但移动而非拷贝对象会大幅度提升性能。

有些类型是不能被拷贝的,如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是正常的变量。


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

所引用的对象将要被销毁

该对象没有用户

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

标准库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函数来将普通迭代器转换为移动迭代器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ c++primer 读书笔记