c++ 右值引用

 

参考

c++11 引入了右值引用, 它是一个非常重要的特性, 允许程序员高效地移动资源而非拷贝资源

通过右值引用, c++ 提供了一个强大的移动语义, 从而显著提高了程序的性能

概念

左值

  • lvalue(loactor value)

表示一个有名字变量或对象, 其存储在内存中有明确存储地址(可寻址), 可以在表达式之后仍然存在

// x是一个左值
int x = 10;

右值

  • rvalue(read value)

表示一个临时值, 通常没有明确内存地址, 也不会在表达式之后存在

例如, 常量10就是右值, 因为它是一个临时、没有名称值, 不会存储在命名内存位置上

// 5和3是右值, 它们和8也是一个右值
int y = 5 + 3;

判断

赋值运算符判断

任何位于赋值运算符(=)左侧的表达式都是左值位于右侧的通常是右值

int a = 5;

// 5 是右值, a 是左值
a = 5;  // a 是左值, 5 是右值

名称和地址判断

左值: 有名称, 可以获取其内存地址

右值: 没有名称, 通常没有明确的内存地址

int a = 5;  // a 是左值
int &b = a; // b 是左值引用

5;  // 5 是右值, 没有名称

引用

左值引用

c++98/03 标准中就有引用, 使用&表示, 但此种引用方式在正常情况下只能操作 c++ 中左值, 无法对右值添加引用

int num = 10;

// 正确
int &b = num;

// 错误: 不能将右值绑定到左值引用
// 编译错误
int &c = 10;

然而, c++98/03 允许常量左值引用绑定到右值, 这使得能够在需要常量的地方使用右值

const int &c = 10;  // 常量左值引用可以绑定到右值

右值引用

c++11 新标准引入右值引用, 用 && 表示, 右值引用使得可以”转移”资源, 而不是复制, 避免不必要的拷贝, 从而实现移动语义

// rvalueRef 是一个右值引用, 绑定到 10
int&& rvalueRef = 10;

右值引用必须在初始化时绑定到一个右值, 且一旦绑定, 右值引用就指向该右值

int num = 10;

// 错误: 不能将左值绑定到右值引用
int &&a = num; // 编译错误

// 正确: 右值引用绑定到右值
int &&b = 10;

应用

移动语义通过右值引用实现, 使得对象可以移动而不是拷贝, 从而提升性能

移动构造函数和移动赋值运算符是移动语义主要实现方式

移动构造函数

移动构造函数允许对象的资源从一个临时对象(右值)转移到新的对象, 而不是复制

class MyClass {
public:
    int* m_data;

    // 构造函数: 为 m_data 分配内存
    MyClass(int value) : m_data(new int(value)) {}

    // 移动构造函数: 从右值引用移动资源
    MyClass(MyClass&& other) : m_data(other.m_data) {
        other.m_data = nullptr;  // 将 other 的资源置为空
    }

    // 析构函数: 释放资源
    ~MyClass() {
        delete m_data;
    }
};

移动构造函数MyClass(MyClass&& other)接收一个右值引用other, 并将其资源(m_data)转移到当前对象, 然后, 将other.mData置为空指针, 避免在析构时释放资源

移动赋值运算符

移动赋值运算符用于将一个右值的资源移动到当前对象, 避免不必要的内存分配和释放

MyClass& operator=(MyClass&& other) {
    if (this != &other) {
        delete m_data;
        m_data = other.m_data;
        other.m_data = nullptr;
    }
    return *this;
}

通过移动赋值运算符, 避免了不必要的资源拷贝, 从而提高了性能

std::move

std::move是一个标准库函数, 它接受一个左值并将其”转换”为右值引用, 从而可以将左值对象资源移动到另一个对象中

std::move本质上并不真正”移动”对象, 它只是将左值转换为右值引用, 使得移动语义可以生效

MyClass a(10);
// a被转换为右值引用, 资源被移动到b
MyClass b = std::move(a);

在上述代码中, std::move(a)将左值a转换为右值引用, 从而触发了移动构造函数, 将a的资源移动到b中