右值引用和对象移动
程序员文章站
2022-03-22 21:37:40
...
右值引用
概念
为了支持移动操作,C++11引入了一种新的引用类型-右值引用。就是必须绑定到右值的引用。我们通过&&而不是&来获得右值引用。右值引用只能绑定到一个将要销毁的对象。
由于右值引用只能绑定到临时对象,我们得知:
所引用的对象将要被销毁
该对象没有其他用户
这两个特性意味着:使用右值引用的代码可以自身*的接管所引用的对象的资源。
变量可以看作只有一个运算对象而没有运算符的表达式。
int i = 23;
int &&j = i;//错误:不能将一个右值引用绑定到一个左值上
int &&k = i * 3;//正确,i*3是一个右值
标准库move函数
虽然不能将一个右值引用直接绑定到一个左值上,但可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用。
int i=6;
int &&l=std::move(i);
对象移动
提出
在很多情况下都会发生对象拷贝,比如利用reallocate重新分配内存的过程,在其中某些情况下,对象拷贝后就立即被销毁了。在这些情况下,移动而非拷贝对象会大幅度提升性能。
移动构造函数和移动赋值运算符
移动构造函数,参数是右值引用
StrVec::StrVec(StrVec &&s) noexcept //noexcept指名不抛出异常
:elements(s.elements), first_free(s.first_free),cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr; //销毁地动源后的对象
}
与拷贝构造函数不同,移动构造函数不分配任何新内存:它接管给定的StrVec中的内存。在接管内存后,它将给定对象中的指针都置为nullptr。
合成的移动操作
与处理拷贝构造函数和拷贝赋值运算符一样,编译器也会合成移动构造函数和移动赋值运算符。但是,合成移动构造的条件与合成拷贝操作的条件大不相同。
只有当一个类没有定义任何自己版本的拷贝控制成员(拷贝构造函数、拷贝赋值运算符、析构函数),且它的所有数据成员都能移动构造或移动赋值时,编译器才会为它合成移动构造函数或移动赋值运算符。
如果一个类定义了一个移动构造函数或一个移动赋值运算符,则该类的合成拷贝构造函数和拷贝赋值运算符被定义为删除的。
右值引用和成员函数
除了构造函数和赋值运算符之外,成员函数也可以同时提供拷贝版本和移动版本。例如:
void push_back(const X&); //拷贝:绑定到任意类型的X
void push_back(X&&); //移动:只能绑定到类型X的可修改的右值