C++ 左值、右值、左值引用、右值引用

发布时间:2026/7/25 19:16:24
C++ 左值、右值、左值引用、右值引用 C 左值、右值、左值引用、右值引用大家好我是你们的技术博主。今天我们来聊聊C中一个让很多初学者头疼但又是进阶必备的概念左值、右值、左值引用和右值引用。别被这些术语吓到我会用最通俗的语言配上可运行的代码帮你一次搞懂。## 一、什么是左值和右值在C中左值lvalue和右值rvalue是根据对象的持久性和可寻址性来区分的。-左值可以取地址的对象通常有名字可以出现在赋值语句的左边。比如变量、数组元素、函数返回的引用等。-右值临时对象不能取地址通常只能出现在赋值语句的右边。比如字面常量3.14、表达式的结果ab、函数返回的非引用值等。用一句话总结左值有持久的内存位置右值是临时的“过客”。### 示例代码1区分左值和右值cpp#include iostreamusing namespace std;int main() { int a 10; // a是左值10是右值字面常量 int b 20; // b是左值 int c a b; // ab的结果是右值临时值 // 错误示例右值不能取地址 // int* p (10); // 编译错误不能对右值取地址 // int* q (a b); // 编译错误不能对临时结果取地址 // 正确示例左值可以取地址 int* p a; // 合法a是左值 cout a的地址: p endl; // 左值可以被赋值 a 100; // 合法a是左值 // 10 a; // 错误10是右值不能出现在左边 cout a的新值: a endl; cout a b的结果右值: a b endl; return 0;}运行结果a的地址: 0x7ffeefbff5a0a的新值: 100a b的结果右值: 120## 二、左值引用左值引用就是绑定到左值的引用。我们平时用的最多的引用类型就是左值引用用表示。### 左值引用的特点- 必须绑定到左值有名字的变量- 不能绑定到右值临时对象- 引用是原变量的别名修改引用会改变原变量### 示例代码2左值引用cpp#include iostreamusing namespace std;int main() { int x 42; // x是左值 int ref x; // ref是左值引用绑定到x cout x x endl; cout ref ref endl; ref 100; // 通过引用修改x cout 修改ref后x x endl; // 错误示例左值引用不能绑定到右值 // int ref2 42; // 编译错误不能将左值引用绑定到右值 // 常量左值引用可以绑定到右值特殊规则 const int cref 42; // 合法const引用可以绑定到临时对象 cout 常量引用绑定到右值: cref endl; return 0;}运行结果x 42ref 42修改ref后x 100常量引用绑定到右值: 42## 三、右值引用右值引用是C11引入的新特性用表示。它专门用来绑定到右值临时对象。右值引用的出现主要是为了实现移动语义和完美转发。### 右值引用的特点- 只能绑定到右值临时对象- 可以延长临时对象的生命周期- 主要用于移动构造函数和移动赋值运算符### 示例代码3右值引用cpp#include iostream#include stringusing namespace std;void processValue(int val) { cout 左值引用版本val val endl;}void processValue(int val) { cout 右值引用版本val val endl;}int main() { int a 10; int b 20; processValue(a); // 调用左值引用版本 processValue(a b); // 调用右值引用版本ab是临时值 processValue(100); // 调用右值引用版本100是字面常量 // 右值引用变量本身是左值 int rref 42; // rref绑定到右值42 // 但rref本身是左值有名字、可寻址 processValue(rref); // 调用左值引用版本因为rref是左值 // 使用std::move可以将左值转为右值 processValue(std::move(a)); // 调用右值引用版本 return 0;}运行结果左值引用版本val 10右值引用版本val 30右值引用版本val 100左值引用版本val 42右值引用版本val 10## 四、为什么需要右值引用右值引用的核心价值在于移动语义。传统的拷贝操作会复制数据而移动操作可以“偷取”临时对象的资源避免不必要的内存拷贝提升性能。### 示例代码4移动语义与拷贝构造cpp#include iostream#include vector#include stringusing namespace std;class MyVector {public: int* data; size_t size; // 构造函数 MyVector(size_t n) : size(n) { data new int[n]; cout 构造函数分配了 n 个整数 endl; } // 拷贝构造函数深拷贝 MyVector(const MyVector other) : size(other.size) { data new int[size]; copy(other.data, other.data size, data); cout 拷贝构造函数深拷贝 size 个整数 endl; } // 移动构造函数偷取资源 MyVector(MyVector other) noexcept : data(other.data), size(other.size) { other.data nullptr; other.size 0; cout 移动构造函数偷取了 size 个整数 endl; } ~MyVector() { delete[] data; cout 析构函数 endl; }};int main() { vectorMyVector vec; cout --- 创建临时对象并插入 --- endl; vec.push_back(MyVector(10)); // 创建临时MyVector然后移动或拷贝 cout \n--- 使用std::move --- endl; MyVector v1(20); vec.push_back(std::move(v1)); // 将左值转为右值触发移动构造 cout \n程序结束 endl; return 0;}运行结果--- 创建临时对象并插入 ---构造函数分配了 10 个整数移动构造函数偷取了 10 个整数析构函数--- 使用std::move ---构造函数分配了 20 个整数移动构造函数偷取了 20 个整数程序结束析构函数析构函数## 五、总结通过本文我们学习了C中左值、右值、左值引用和右值引用的核心概念1.左值有持久内存位置的对象可以取地址通常有名字。2.右值临时对象不能取地址通常是字面常量或表达式结果。3.左值引用绑定到左值的引用用表示不能绑定到右值除了const引用。4.右值引用绑定到右值的引用用表示是C11的新特性用于实现移动语义。右值引用的出现让C程序员能够更精细地控制资源管理通过移动语义避免不必要的拷贝显著提升程序性能。在实际开发中合理使用std::move和移动构造函数可以让你的代码既高效又优雅。记住一句话左值引用绑定到“有家”的对象右值引用绑定到“流浪”的临时对象。理解了这个比喻你就掌握了这些概念的精髓。