C++ 左值引用 (&
) & 右值引用 (&&
) 详解
C++ 中的左值引用 (&
) 和 右值引用 (&&
) 用于 操作变量(对象) ,控制 生命周期、优化拷贝 和 提升性能。
1. 什么是左值(Lvalue)和右值(Rvalue)
(1)左值(Lvalue)
✅ 可以取地址 &
,并且能持续存在 (即生命周期长)。
✅ 可以出现在赋值号(=
)左侧,可被修改。
cpp
int a = 10; // ✅ `a` 是左值
a = 20; // ✅ 左值可以赋值
常见左值
- 变量 (
int x
、std::string name
) - 数组元素 (
arr[0]
) - 解引用指针 (
*ptr
) - 返回左值引用的函数 (
int& foo()
)
(2)右值(Rvalue)
✅ 不能取地址 &
,是临时的、短生命周期 的对象。
✅ 只能出现在赋值号(=
)右侧,不能再赋值。
cpp
int x = 5 + 10; // ✅ `5 + 10` 是右值
常见右值
- 字面量(
10
,'c'
,3.14
) - 临时变量(
a + b
) - 返回非引用的函数(
int foo()
)
❌ 错误示例
cpp
&(5 + 10); // ❌ 右值不能取地址
(5 + 10) = 42; // ❌ 右值不能赋值
2. 左值引用(Lvalue Reference, &
)
✅ 左值引用可以绑定左值,让变量通过别名访问。
cpp
int a = 10;
int& ref = a; // ✅ 左值引用,ref 绑定 a
ref = 20; // ✅ 修改 ref 也会修改 a
⚠️ 不能绑定右值
cpp
int& r = 5; // ❌ 不能用左值引用绑定右值
3. 右值引用(Rvalue Reference, &&
)
✅ 右值引用只能绑定右值,允许"偷" 资源,提高性能。
cpp
int&& r = 5; // ✅ 右值引用
r = 10; // ✅ `r` 仍然是变量,可以修改
⚠️ 不能绑定左值
cpp
int a = 10;
int&& r = a; // ❌ 不能用右值引用绑定左值
✅ 用 std::move()
转换左值为右值
cpp
int&& r = std::move(a); // ✅ `a` 变成右值
4. 右值引用的用途
(1)移动语义(避免深拷贝,提高性能)
cpp
#include <iostream>
#include <string>
class MoveExample {
public:
std::string data;
// 右值引用的移动构造函数
MoveExample(std::string&& str) : data(std::move(str)) {
std::cout << "Move Constructor Called" << std::endl;
}
};
int main() {
std::string temp = "Hello";
MoveExample obj(std::move(temp)); // ✅ 调用移动构造
return 0;
}
⚡ 右值引用避免了 temp
的深拷贝,提升效率!
(2)完美转发(std::forward
)
std::forward<T>(t)
保留 t
的左值或右值特性。
cpp
template <typename T>
void wrapper(T&& arg) {
func(std::forward<T>(arg)); // ⚡ 传递 `arg` 的原始类型
}
🔹 std::forward<T>
保留参数原始状态,适用于泛型编程。
5. 左值 & 右值 & 引用 总结
类型 | 绑定左值 | 绑定右值 | 示例 |
---|---|---|---|
左值(Lvalue) | ✅ | ❌ | int a = 10; |
右值(Rvalue) | ❌ | ✅ | 5 + 10 |
左值引用(int& ) |
✅ | ❌ | int& ref = a; |
右值引用(int&& ) |
❌ | ✅ | int&& r = 5; |
🚀 右值引用 (&&
) 是 C++11 的核心优化工具!
常用于:
- 移动语义(提高性能,减少拷贝)
std::move()
(强制转换为右值)std::forward<T>()
(完美转发)
C++ 移动构造(Move Constructor)详解
1. 什么是移动构造?
移动构造(Move Constructor) 是 C++11 引入的一种优化机制 ,用于高效转移对象的资源,避免昂贵的深拷贝(Deep Copy)。
📌 关键点
T(T&& other)
接受右值引用(&&
)。- 转移资源所有权 ,避免拷贝大对象。
- 清空原对象,避免释放同一块内存(
other
置空)。
2. 移动构造 vs. 复制构造
(1)复制构造(深拷贝)
cpp
class Example {
public:
int* data;
// 复制构造函数(深拷贝)
Example(const Example& other) {
data = new int(*other.data); // 拷贝数据
}
};
✅ 问题:
- 拷贝对象的所有数据 ,新分配内存 ,性能较低。
(2)移动构造(转移资源,不拷贝)
cpp
class Example {
public:
int* data;
// 移动构造函数
Example(Example&& other) noexcept : data(other.data) {
other.data = nullptr; // ✅ 避免释放同一块内存
}
};
✅ 优势:
- 资源直接转移(不重新分配内存)。
- 性能更高 (适合大对象,如
std::vector
)。
3. 移动构造函数的实现
cpp
#include <iostream>
class Example {
private:
int* data;
public:
// 构造函数
Example(int value) : data(new int(value)) {
std::cout << "Constructor: allocated " << *data << std::endl;
}
// 移动构造函数
Example(Example&& other) noexcept : data(other.data) {
other.data = nullptr; // 清空原对象,避免释放
std::cout << "Move Constructor: moved resource" << std::endl;
}
// 析构函数
~Example() {
if (data) {
std::cout << "Destructor: freeing " << *data << std::endl;
delete data;
} else {
std::cout << "Destructor: nothing to free" << std::endl;
}
}
};
int main() {
Example a(10);
Example b(std::move(a)); // ✅ 调用移动构造
return 0;
}
输出
Constructor: allocated 10
Move Constructor: moved resource
Destructor: nothing to free
Destructor: freeing 10
✅ b
拿走了 a
的资源,a
被置空,避免二次释放!
4. std::move()
的作用
std::move()
将左值转换为右值 ,触发移动构造。
cpp
Example a(100);
Example b = a; // ❌ 复制构造(慢)
Example c = std::move(a); // ✅ 移动构造(快)
🔥 std::move(a)
强制 a
变为右值 ,使 c
调用 移动构造 ,而不是 复制构造。
5. 什么时候调用移动构造?
(1)返回局部对象(C++11 及更高版本,RVO 可能优化)
cpp
Example createExample() {
return Example(200); // ✅ 可能触发移动构造
}
Example obj = createExample(); // ✅ 调用移动构造
(2)存入 std::vector
(避免拷贝,提高性能)
cpp
std::vector<Example> vec;
vec.push_back(Example(300)); // ✅ 直接移动构造,提高效率
(3)手动 std::move()
转换左值
cpp
Example a(400);
Example b = std::move(a); // ✅ 触发移动构造
6. 复制 & 移动的完整实现
cpp
#include <iostream>
class Example {
private:
int* data;
public:
// 构造函数
Example(int value) : data(new int(value)) {}
// 复制构造
Example(const Example& other) : data(new int(*other.data)) {
std::cout << "Copy Constructor: deep copy" << std::endl;
}
// 移动构造
Example(Example&& other) noexcept : data(other.data) {
other.data = nullptr;
std::cout << "Move Constructor: moved" << std::endl;
}
~Example() {
if (data) delete data;
}
};
int main() {
Example a(100);
Example b = a; // ❌ 复制构造(慢)
Example c = std::move(a); // ✅ 移动构造(快)
return 0;
}
7. std::vector
移动构造优化
cpp
#include <vector>
#include <iostream>
class Example {
public:
Example() { std::cout << "Default Constructor" << std::endl; }
Example(const Example&) { std::cout << "Copy Constructor" << std::endl; }
Example(Example&&) noexcept { std::cout << "Move Constructor" << std::endl; }
};
int main() {
std::vector<Example> vec;
vec.reserve(3); // 预分配内存,防止 `realloc` 触发拷贝构造
vec.push_back(Example()); // ✅ 直接使用右值,调用移动构造
return 0;
}
输出
Default Constructor
Move Constructor
✅ push_back(Example())
直接移动构造,不调用拷贝构造,提高性能!
8. 移动构造 vs. 复制构造
操作 | 复制构造(深拷贝) | 移动构造(转移所有权) |
---|---|---|
数据存储 | 复制一份新数据 | 直接转移指针 |
效率 | 慢(分配新内存) | 快(不分配新内存) |
适用场景 | 需要保留原数据 | 只需转移资源 |
函数签名 | T(const T&) |
T(T&&) noexcept |
9. 结论
🚀 移动构造比拷贝构造更高效,用于:
- 返回临时对象 (如
return Example();
)。 - 避免
std::vector
复制大对象 (emplace_back()
)。 - 手动
std::move()
强制触发移动构造。