马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Move semantics 移动语义
[!tip]
在 C++98 中,所有的对象拷贝都使用了 拷贝构造函数 或 拷贝赋值运算符,这通常需要深拷贝资源,比方动态分配的内存或文件句柄。这会导致性能开销,尤其是对于临时对象而言(如函数返回值)。
为了解决这个问题,C++11 引入了移动语义,通过区分“拷贝”和“移动”两种语义,大幅提高性能
左值和右值
- 左值(Lvalue): 指向内存中的某个具体位置,具有持久性
- 右值(Rvalue): 临时值,通常是没有持久性的
- 示例:字面值 10 或临时对象 a + b 的结果
C++11 中,右值被细化为 纯右值(prvalue) 和 亡值(xvalue),此中亡值用于表示即将失去所有权的对象(如返回值)
移动构造函数和移动赋值运算符
- 移动构造函数:允许从另一个对象中“窃取”资源,而不是复制资源。
- 移动赋值运算符:将一个对象的资源转移给另一个对象,而不需要深拷贝。
- class MyClass {
- int* data;
- public:
- // 构造函数
- MyClass(int val) : data(new int(val)) {}
- // 移动构造函数
- MyClass(MyClass&& other) noexcept : data(other.data) {
- other.data = nullptr; // 释放所有权
- }
- // 移动赋值运算符
- MyClass& operator=(MyClass&& other) noexcept {
- if (this != &other) {
- delete data; // 释放原有资源
- data = other.data; // 转移所有权
- other.data = nullptr;
- }
- return *this;
- }
- ~MyClass() { delete data; }
- };
复制代码 标准库函数
C++11
Rvalue references 右值引用
C++11 新增了一种范例引用:右值引用,使用 && 表示,右值引用只绑定到右值。- int x = 0; // `x` is an lvalue of type `int`
- int& xl = x; // `xl` is an lvalue of type `int&`
- int&& xr = x; // compiler error -- `x` is an lvalue
- int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
- void f(int& x) {}
- void f(int&& x) {}
- f(x); // calls f(int&)
- f(xl); // calls f(int&)
- f(3); // calls f(int&&)
- f(std::move(x)); // calls f(int&&)
- f(xr2); // calls f(int&)
- f(std::move(xr2)); // calls f(int&& x)
复制代码 右值引用的紧张目的是:
- 接收和操作右值(如临时对象)。
- 实现 移动语义 和 完美转发。
Forwarding references 转发引用
也被称为Universal References(万能引用、通用引用),与std::forward一起使用实现完美转发(Perfect Forwarding。转发引用是通过语法 T&& 创建的,此中 T 是模板范例参数,或使用 auto&&
紧张作用是:根据传入参数的值类别(左值或右值)保持其原始类别,从而精确地传递给另一个函数(比方,左值保持为左值,临时对象作为右值转发)
转发引用允许引用根据范例绑定到左值或右值。转发引用遵循引用折叠规则:
- T& & 酿成 T&
- T& && 酿成 T&
- T&& & 酿成 T&
- T&& && 酿成 T&&
auto 范例推导与左值和右值:- int x = 0; // `x` is an lvalue of type `int`
- auto&& al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
- auto&& ar = 0; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
复制代码 带有左值和右值的模板范例参数推导:- // Since C++14 or later:
- void f(auto&& t) {
- // ...
- }
- // Since C++11 or later:
- template <typename T>
- void f(T&& t) {
- // ...
- }
- int x = 0;
- f(0); // T is int, deduces as f(int &&) => f(int&&)
- f(x); // T is int&, deduces as f(int& &&) => f(int&)
- int& y = x;
- f(y); // T is int&, deduces as f(int& &&) => f(int&)
- int&& z = 0; // NOTE: `z` is an lvalue with type `int&&`.
- f(z); // T is int&, deduces as f(int& &&) => f(int&)
- f(std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
复制代码[!note]
有int x = 10,为什么int&& y = x会出错,而auto&& y = x却没有问题?
- int && y = x中,x是一个左值,而y是一个右值引用,右值引用只能绑定右值
- auto&& y = x中,auto&&是一个万能引用,x是左值时,其会主动推导为int&,x是右值时,其会主动推到为int&&,所以该式子会主动推导为int& y = x
标准库函数
C++11
用于移动语义的特殊成员函数
复制构造函数和复制赋值运算符在进行复制时被调用,而随着 C++11 引入移动语义,如今有了用于移动的移动构造函数和移动赋值运算符。目的是提高性能,特殊是当操作涉及临时对象时
移动构造函数
移动构造函数的作用是“窃取”另一个对象的资源,而不是复制资源,从而避免了不必要的深拷贝操作。
特点:
- 担当一个右值引用(T&&)作为参数。
- 通常会转移资源的所有权。
- 被移动的对象(源对象)会进入“有效但无用”的状态(如将指针置为 nullptr)
[code]#include #include // for std::moveclass MyClass { int* data;public: // 构造函数 MyClass(int val) : data(new int(val)) { std::cout |