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()强制触发移动构造。