右值引用与移动语义

右值引用与移动语义

右值引用移动语义C++11 里非常核心的一组能力。它们解决的问题很实际:避免对象在传递和返回时发生不必要的深拷贝。

1. 什么是右值引用?

右值引用的语法是 &&

1
int&& x = 10;

这里的 10 是一个右值,x 用右值引用绑定了它。

2. 它为什么重要?

在老 C++ 里,大对象传来传去通常依赖拷贝。
比如一个 std::vector 或大字符串,拷贝成本可能很高。

C++11 通过右值引用区分出“这个对象马上就不用了”,于是资源就可以直接“搬走”,而不是重新复制一份。

3. 移动和拷贝的区别

拷贝

  • 复制资源
  • 原对象和新对象各有一份独立数据

移动

  • 转移资源所有权
  • 原对象通常变成“空但有效”的状态

4. 一个简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <vector>
using namespace std;

int main()
{
vector<int> a{1, 2, 3, 4};
vector<int> b = std::move(a);

cout << "a size: " << a.size() << endl;
cout << "b size: " << b.size() << endl;
return 0;
}

这里 b 直接接管了 a 的资源。

5. 移动构造和移动赋值

如果你自己写类,通常可以提供:

1
2
ClassName(ClassName&& other);
ClassName& operator=(ClassName&& other);

这样对象在需要移动时,就能走更高效的路径。

6. 一个自定义类例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

class Buffer
{
public:
Buffer(size_t size) : size_(size), data_(new int[size]) {}

Buffer(Buffer&& other) noexcept
: size_(other.size_), data_(other.data_)
{
other.size_ = 0;
other.data_ = nullptr;
}

~Buffer()
{
delete[] data_;
}

private:
size_t size_{0};
int* data_{nullptr};
};

这里移动构造并没有重新分配和复制内存,而是直接接管了资源。

7. std::move 是什么?

std::move 本身并不会移动对象,它只是把对象显式转换成右值引用,让编译器有机会调用移动构造或移动赋值。

1
2
std::string a = "hello";
std::string b = std::move(a);

8. 使用时要注意的点

8.1 被 move 之后的对象还能用,但状态未指定

通常只能假设它“有效但值不确定”。
最稳妥的做法是只做重新赋值或销毁。

8.2 不要滥用 std::move

如果对象后面还要继续正常使用,那就别急着 move

8.3 移动语义最适合资源型对象

比如:

  • 容器
  • 智能指针
  • 自定义句柄类

总结

右值引用和移动语义是现代 C++ 性能模型里非常关键的一部分。
它们的本质不是“语法更复杂了”,而是给对象传递增加了一条更高效的路径。理解 std::move、移动构造和资源转移,对写现代 C++ 很重要。