前置知识
左值:表达式结束后依然存在的持久对象(在内存中占有确定位置)
右值:表达式结束时不再存在的临时对象(不在内存中占有确定位置)
1 | int val; // 对变量val进行了定义,故在栈上会给val分配内存地址 |
std::move函数
- 将一个左值转换成右值引用,从而可以调用右值引用的拷贝构造函数
- 针对有在堆上为对象分配内存的情况
- 该函数仅仅做了类型转换(可理解为static_cast转换),对性能无影响。
- 真正的移动操作在移动构造函数或者移动赋值操作符中发生,我们可以在自己的类中实现移动语义,避免深拷贝
- 被move的值有没有失效,关键看有没有调用移动构造函数,或者移动复制运算符
- 单纯的
Foo && f = std::move (x)
; 是不会调用移动构造或者移动赋值运算符的。 只是把右值给绑定到了f
上。 Foo t = std::move (x)
, 这样才会调用移动构造函数
- 单纯的
Why std::move
右值引用和std::move
广泛应用于在STL和自定义类中实现移动语义,避免拷贝,从而提升程序性能
在没有右值引用之前,一个简单的数组类通常实现如下:
1 | class Array { |
改进:提供一个移动构造函数
,把被拷贝者的数据移动过来,之后丢弃被拷贝者,从而避免深拷贝
1 | class Array { |
这样做有三个问题:
- 不优雅,表示移动语义需要一个额外的参数
temp_array
是const左值引用,无法被修改为nullptr
- 即使把函数参数改成非const
Array& temp_array
,由于左值引用不能接右值,无法使用Array a = Array(Array(), true);
最终,使用右值引用
1 | int main(){ |
std::move在STL容器中的应用
在STL的很多容器中,都实现了以右值引用为参数的移动构造函数和移动赋值重载函数,或者其他函数
最常见的如std::vector的push_back
和emplace_back
。参数为左值引用意味着拷贝,为右值引用意味着移动。
有些STL类只有移动构造函数,比如unique_ptr
,因此只能移动(转移内部对象所有权,或者叫浅拷贝),不能深拷贝