📘 C++ 移动语义与 std::move() 教程
C++的std::move用于转移变量/对像的所有权/Ownership。
🔹 什么是移动语义?
在 C++ 中,移动语义通过转移资源所有权/Ownership(如内存或文件句柄)来优化性能,而不是复制它们。
移动语义是在 C++11 中引入的,它允许:
- 更快速地传递大型或昂贵的对象
- 更高效地使用临时值
🔹 什么是 std::move()?
std::move(x) 并不会真的移动任何东西 —— 它只是将 x 转换为一个 右值引用(即 T&&),告诉编译器:
“你可以把这个对象当作临时对象来处理并移动它。”
要真正实现移动,你的类型必须实现 移动构造函数 或 移动赋值运算符。
✅ 什么时候该用 std::move()?
在以下情况下使用它:
- 你想 转移资源的所有权。
- 你正在处理 复制开销大的对象(如
std::string、std::vector、unique_ptr)。 - 你写的函数按值接收参数,并希望将其移动进成员变量。
🔍 std::string 示例
#include <iostream>
#include <string>
#include <utility>
int main() {
std::string a = "hello";
std::string b = std::move(a);
std::cout << "b: " << b << std::endl;
std::cout << "a: " << a << std::endl;
}
🔍 移动 std::vector
std::vector<int> original = {1, 2, 3};
std::vector<int> moved_to = std::move(original);
// original 现在为空(但仍然有效)
⚠️ 移动后会发生什么?
移动后:
- 被移动的对象 仍然有效。
- 但其 内容未定义 —— 你只能销毁它或重新赋值。
std::string x = "abc";
std::string y = std::move(x);
// x 现在处于有效但未定义的状态 —— 不要再读取它!
🧠 对内建类型使用 std::move()
int x = 42;
int y = std::move(x); // 实际是拷贝,因为 int 没有移动语义
这没必要,因为像 int 这样的基本类型不支持移动构造。
🛠️ 自定义类型实现移动语义
class MyBuffer {
int* data;
size_t size;
public:
MyBuffer(size_t s) : size(s), data(new int[s]) {}
// 移动构造函数
MyBuffer(MyBuffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 移动赋值运算符
MyBuffer& operator=(MyBuffer&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
~MyBuffer() { delete[] data; }
};
使用示例:
MyBuffer a(1000);
MyBuffer b = std::move(a); // 将 a 移动到 b
📦 std::move() 与智能指针
可以用 std::move来操作智能指针,比如 unique_ptr 或 shared_ptr:
#include <memory>
std::unique_ptr<int> p1 = std::make_unique<int>(10);
std::unique_ptr<int> p2 = std::move(p1);
// p1 现在为空指针
🔁 std::shared_ptr 所有权转移
当你“转移所有权”给另一个 shared_ptr 时,你实际上是:
- 将控制块(用于跟踪引用计数)从一个
shared_ptr移动到另一个。 - 原来的
shared_ptr变为空(use_count() == 0)。 - 总体引用计数不变(仍为 1,除非还有其他共享所有者)。
✅ 示例:通过 std::move() 转移所有权
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> p1 = std::make_shared<int>(42);
std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 1
std::shared_ptr<int> p2 = std::move(p1); // 转移所有权
std::cout << "p1 is " << (p1 ? "not null" : "null") << std::endl; // null
std::cout << "p2 use_count: " << p2.use_count() << std::endl; // 1
}
🔍 重要区别:shared_ptr vs unique_ptr
| 指针类型 | 转移机制 | 允许拷贝 | 主要用途 |
|---|---|---|---|
std::unique_ptr |
仅支持 std::move() |
❌ 不允许 | 独占资源所有权 |
std::shared_ptr |
std::move() 或拷贝 |
✅ 允许 | 共享资源所有权,引用计数 |
⚠️ 注意事项
- 你可以使用 move 来转移
shared_ptr的所有权(源指针将变为空)。 - 你也可以拷贝
shared_ptr来共享所有权(两个指针都有效,引用计数增加)。 - 只有在你明确希望原来的
shared_ptr被置空时才使用std::move()。
🔄 常见使用模式
函数返回值使用移动:
std::string get_name() {
std::string name = "Alice";
return std::move(name);
}
只有在你想强制进行移动(比如返回函数参数)时才使用 std::move()。
🚫 不该使用 std::move() 的场景
1. ❌ 不要从还需要使用的变量移动:
std::string s = "test";
std::string t = std::move(s);
std::cout << s; // 内容未定义
2. ❌ 不要对 const 对象使用 std::move():
const std::string s = "hi";
std::string t = std::move(s); // 实际是拷贝,因为移动构造函数无法接收 const 参数
🧪 总结速查表
| 使用场景 | 是否使用 std::move() | 原因 |
|---|---|---|
| 移动大型容器或字符串 | ✅ 是 | 高效转移内存或资源 |
| 移动智能指针 | ✅ 是 | 转移所有权 |
| 基本类型(如 int、bool) | 🚫 否 | 没有移动语义,等同于拷贝 |
| const 对象 | 🚫 否 | 移动构造函数不接受 const |
| 临时变量 | 🚫 通常不需要 | 已经是右值了 |
✅ 最后小贴士
如果你不确定该不该用 std::move(),问自己:
“我是否不再需要这个变量并打算把它交出去?”
如果答案是“是” → 那就用 std::move()。
C/C++编程
- 理解C++中的std::transform_reduce及示例
- 使用原子 TAS 指令实现自旋锁
- C++中检测编译时与运行时: if consteval 与 std::is_constant_evaluated()
- C++ 转发引用: 完美转发的关键
- 理解 C++ 中的 dynamic_cast: 安全的向下转型与向上转型
- C与C++: restrict关键字及其在编译器优化中的作用
- C++的左值/lvalue, 右值/rvalue和右值引用/rvalue references
- C++中的assert和static_assert的区别
- C++: auto_ptr智能指针被弃用
- C++中的consteval是什么? 它与const和constexpr有何不同?
- C++ 教程: 用std::move来移动所有权
- C++中的 const和constexpr 比较
- 简易教程: C++的智能指针
- C++ 编程练习题: 如何合并两个二叉树?
- C++ 编程练习题 - 找出第三大的数
- C++ 编程练习题 - 最多连续的 1
- C++ 编程练习题 - 左子树叶节点之和 (深度优先+广度优先+递归)
- C++ 编程练习题 - 最多水容器 (递归)
- C++的异步编程: std::future, std::async 和 std::promise
- C编程练习题: 翻转整数位
- C++编程练习题: 找出字符串的所有大小小组合
- C/C++ 中的内存管理器(堆与栈)
- C++编程练习题: 对两单向链表求和
英文:Tutorial on C++ std::move (Transfer Ownership)
强烈推荐
- 英国代购-畅购英伦
- TopCashBack 返现 (英国购物必备, 积少成多, 我2年来一共得了3000多英镑)
- Quidco 返现 (也是很不错的英国返现网站, 返现率高)
- 注册就送10美元, 免费使用2个月的 DigitalOcean 云主机(性价比超高, 每月只需5美元)
- 注册就送10美元, 免费使用4个月的 Vultr 云主机(性价比超高, 每月只需2.5美元)
- 注册就送10美元, 免费使用2个月的 阿里 云主机(性价比超高, 每月只需4.5美元)
- 注册就送20美元, 免费使用4个月的 Linode 云主机(性价比超高, 每月只需5美元) (折扣码: PodCastInit2022)
- PlusNet 英国光纤(超快, 超划算! 用户名 doctorlai)
- 刷了美国运通信用卡一年得到的积分 换了 485英镑
- 注册就送50英镑 – 英国最便宜最划算的电气提供商
- 能把比特币莱特币变现的银行卡! 不需要手续费就可以把虚拟货币法币兑换
微信公众号: 小赖子的英国生活和资讯 JustYYUK