Bootstrap

C++ 左值右值引用详解

1. 左值(lvalue)

   定义:左值是指表达式结束后依然存在的持久对象。简单来说,左值可以出现在赋值语句的左边,它代表一个在内存中有确定存储地址的对象。

   示例:

     变量是典型的左值。例如,`int a = 5;`,这里的`a`是左值。因为`a`有自己的内存地址,并且在这条语句执行后,`a`依然存在,可以被后续的代码访问和修改,如`a = 10;`。

     解引用指针得到的结果也是左值。例如,`int* p = new int(5); *p = 10;`,`*p`就是左值,因为它代表的是通过指针`p`所指向的内存空间中的值,这个值在内存中有确定的位置,并且可以被修改。

2. 右值(rvalue)

   定义:右值是指表达式结束后就不再存在的临时对象。右值通常是字面常量或者是一些临时的计算结果,不能出现在赋值语句的左边。

   示例:

     字面常量是右值。比如`5`、`3.14`、`'a'`等,它们本身是常量,没有自己的存储地址(从某种意义上来说,它们存储在代码段的常量区,但是不能像变量一样被修改),不能被赋值,如`5 = 10;`是非法的。

     表达式的返回值如果是临时对象,一般也是右值。例如,`int add(int a, int b) {return a + b;}`,`add(3, 4)`这个表达式的返回值是一个临时的计算结果,是右值。它在表达式执行完后就不再存在,不能被赋值,如`add(3, 4)= 10;`是非法的。

3. 左值引用(lvalue reference)

   定义:左值引用是给左值起的一个别名,通过`&`符号来定义。它主要用于在函数参数传递或者函数返回值等场景中,避免对象的拷贝,提高效率。

   示例:

     函数参数为左值引用。例如,`void func(int& a) {a++;}`,在调用`int b = 5; func(b);`时,`func`函数中的`a`是`b`的别名,对`a`的修改就是对`b`的修改。

     作为返回值的左值引用。例如,`int& getValue() {static int a = 5; return a;}`,`getValue`函数返回一个对静态变量`a`的引用。可以这样使用`int& ref = getValue(); ref++;`,这里`ref`是`a`的别名,通过`ref`可以修改`a`的值。

4. 右值引用(rvalue reference)

   定义:右值引用是C++11引入的新特性,用于绑定到右值,通过`&&`符号来定义。它的主要目的是实现移动语义和完美转发,提高程序的性能,特别是在处理临时对象时。

   示例:

     实现移动语义。例如,定义一个类`MyString`来模拟字符串,有一个指针成员`char* data`来存储字符串内容。传统的拷贝构造函数和赋值运算符会进行深拷贝,而通过右值引用可以实现移动构造函数和移动赋值运算符。

      

 class MyString {

       public:

           char* data;

           MyString(const MyString& other) {

               // 深拷贝构造函数

               int len = strlen(other.data);

               data = new char[len + 1];

               strcpy(data, other.data);

           }

           MyString(MyString&& other) noexcept {

               // 移动构造函数

               data = other.data;

               other.data = nullptr;

           }

       };

       当使用`MyString func() {MyString temp("hello"); return temp;}`时,在C++11之前,返回`temp`会调用拷贝构造函数进行深拷贝。但有了右值引用后,会调用移动构造函数,将`temp`中的资源(`data`指针)移动到返回的对象中,避免了不必要的拷贝,提高了效率。

     完美转发。在模板函数中,右值引用可以实现完美转发,将参数按照原始的类型(左值或右值)传递给其他函数。例如,`template<typename T> void forwarder(T&& arg) {innerFunc(std::forward<T>(arg));}`,这里`std::forward`可以根据`arg`是左值还是右值,以正确的方式转发给`innerFunc`。

在C++编程中,正确理解和使用左值、右值、左值引用和右值引用可以优化程序性能,特别是在处理对象的生命周期和资源管理方面有很大的帮助。

;