C++ move()函数讲解(轻松理解版)
std::move 通俗理解版
这篇文档的目标很简单:
- 不追求讲全
- 只追求把
std::move最核心的意思讲明白
如果你学完之后能真正理解下面这句话,这篇就算成功了:
std::move本身并不会移动数据,它更像是一个“允许资源转移”的标记。
1. 先记住最核心的理解
std::move 并不是“把数据一格一格搬过去”。
它更准确的意思是:
告诉编译器:这个对象后面的资源,可以转移给别人了。
也就是说:
std::move(A)不是立刻移动数据- 它只是把
A标记成“这个对象可以被拿去转移资源” - 真正发生资源转移,是在后面的移动构造、移动赋值里
所以你可以先把它理解成:
std::move(A)
约等于:
“A 后面的资源我不打算继续留着用了,你可以交给别人”
2. 左值和右值,只讲最够用的版本
理解 std::move,必须先知道左值和右值。
2.1 左值
左值就是:
- 有名字
- 可以持续存在
- 后面还能继续使用
例如:
int a = 10;
int b = a;
这里:
a是左值b是左值
因为它们都有名字,你后面还能继续访问它们。
2.2 右值
右值就是:
- 临时值
- 马上就能拿来用
- 用完通常就没了
例如:
int a = 10;
int b = a + 5;
这里:
5是右值a + 5的结果也是右值
因为它们都是临时算出来的值。
2.3 为什么要把左值变成右值
因为一个有名字的变量,默认编译器会觉得:
这个变量你后面可能还要继续用
所以它默认不会随便把这个变量的资源交出去。
例如:
string A = "hello";
string B = A;
这里 A 是左值,所以默认是拷贝。
但如果你明确知道:
A后面不重要了A原来的资源可以交给B
那你就可以写:
string B = std::move(A);
这时候你是在告诉编译器:
虽然
A有名字,本来是左值,但现在你可以把它当成右值处理。
3. move(A) 到底发生了什么
这是最关键的部分。
看代码:
string A = "hello";
string B = std::move(A);
很多初学者会问:
- 是
A的堆内存移动了? - 是
A的变量内容交给B了? - 是
A整个对象都给B了?
3.1 正确理解
最准确的说法是:
对于像
string、vector这种内部会在堆上申请内存的对象,move 通常会把“那块已经申请好的堆内存的控制权”直接交给新的对象。
也就是:
A原来内部管理着一块堆内存B不再重新申请一块新的堆内存- 而是直接接管
A原来那块堆内存
所以你说的:
“直接用原来的内存地址”
这个理解基本是对的。
本质上就是:
资源所有权转移
3.2 不是 A 整个对象消失了
这一点一定要分清。
std::move(A) 之后,不是说:
A没了A整个变量变成B
不是这样的。
真实情况是:
A这个对象还在B这个对象也在- 只是
A原来管理的资源,交给了B
3.3 所以到底是“资源交过去了”,不是“对象整体交过去了”
这句话你可以直接背:
move(A)不是把 A 这个对象整个交给 B,而是把 A 管理的资源交给 B。
例如对 string 来说,这个“资源”通常就是:
- 字符串数据所在的堆内存
- 长度信息对应的管理权
- 内部缓冲区的所有权
3.4 原对象会变成什么状态
移动之后,原对象通常会变成一个“空壳”状态。
比如:
- 字符串长度可能变成 0
vector可能变成空容器
但是要注意:
标准并不强制要求它一定是空,只保证它仍然是合法对象。
也就是说:
- 对象本身还存在
- 还能析构
- 还能重新赋值
- 还能继续使用
只是:
- 原来的值不要再依赖
所以初学时你可以先把它记成:
move 之后原对象通常像个空壳,但还能继续用。
4. 为什么拷贝慢,移动快
这也是最重要的一部分。
5. 先看普通拷贝做了什么
例如:
string A = "hello";
string B = A;
如果是普通拷贝,通常会发生这些事:
B自己重新申请一块新的堆内存- 把
A里的数据复制到B那块新内存里 - 最后
A和B各自有一份独立的数据
这意味着:
- 要重新分配内存
- 要复制数据
- 如果数据很大,这个成本就很高
所以拷贝的本质是:
再造一份一样的资源
6. 再看 move 做了什么
例如:
string A = "hello";
string B = std::move(A);
move 的时候,通常做的是:
- 不重新申请新的大块堆内存
- 直接把
A原来那块内存的控制权交给B A自己变成一个空壳或未指定但合法的状态
所以 move 的本质是:
不复制资源,而是转移资源所有权
这就是它快的原因。
因为它省掉了:
- 重新申请堆内存
- 拷贝大量数据
7. 一句话区分拷贝和移动
这句最适合背:
拷贝是“重新搞一份新的”,移动是“直接把原来的资源接过来”。
8. 什么情况下 move 会明显更快
结论先说:
当对象内部管理了比较大的资源时,move 往往会比拷贝快很多。
最典型的就是这些:
- 很长的
string - 很大的
vector - 动态数组
- 管理文件句柄的对象
- 管理 socket 的对象
- 管理堆内存的自定义类
为什么这些场景更明显?
因为这些对象背后通常不是一个小整数,而是:
- 一大块堆内存
- 一份系统资源
- 一大批元素
如果用拷贝,就得:
- 重新申请资源
- 再完整复制一遍
如果用 move,就只需要:
- 把所有权转走
所以差距会很明显。
9. 什么情况下 move 不一定有优势
如果对象特别小,比如:
int a = 10;
int b = std::move(a);
这几乎没有意义。
因为:
int本来就只占几个字节- 拷贝一个
int的成本几乎可以忽略
所以:
move 的优势主要体现在“大对象”和“资源对象”上。
10. 一段最直观的代码
下面这段代码就很适合建立直觉:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<string> A;
for (int i = 0; i < 100000; i++) {
A.push_back(string(100, 'a'));
}
vector<string> copyB = A; // 拷贝
vector<string> moveB = std::move(A); // 移动
cout << "copyB size = " << copyB.size() << endl;
cout << "moveB size = " << moveB.size() << endl;
cout << "A size = " << A.size() << endl;
}
10.1 这段代码里为什么 move 更快
因为:
vector<string> copyB = A;
通常意味着:
copyB要重新分配自己的内存- 里面的 10 万个字符串都要复制一遍
而:
vector<string> moveB = std::move(A);
通常意味着:
moveB直接接管A原来的内部资源- 不需要把那 10 万个字符串重新复制一遍
所以在这种情况下,move 通常会明显快很多。
11. 一个更短的 string 例子
#include <iostream>
#include <string>
using namespace std;
int main() {
string A = "hello world";
string B = std::move(A);
cout << "B = " << B << endl;
A = "new value";
cout << "A = " << A << endl;
}
这个例子想说明两件事:
B接管了原来A的资源A虽然被 move 过,但对象本身还存在,还能重新赋值继续使用
12. 你可以直接这样理解 std::move
下面这段话,就是这篇最想让你带走的理解:
std::move本身并不会移动数据,它更像是一个“允许资源转移”的标记。对于像string、vector这种内部会在堆上申请内存的对象,普通拷贝会重新申请一块新的堆内存,再把数据复制过去;而 move 的时候,会把“那块已经申请好的堆内存的控制权”直接转交给新的对象,因此不用重新分配和复制大量数据,所以速度快很多。移动之后,原对象通常会变成“空壳”状态,比如字符串长度变成 0,但对象本身还存在、还能继续赋值使用。所谓“直接用原来的内存地址”,这个理解基本是对的,本质就是资源所有权转移。
13. 最后只背这 4 句话
1. std::move 不是搬数据,而是允许资源转移。
2. move 后不是对象没了,而是对象管理的资源通常转给别人了。
3. 拷贝要重新申请内存并复制数据,move 通常不用。
4. 当对象内部资源很大时,move 往往比拷贝快很多。
更多推荐

所有评论(0)