【5minC++基本功】——左值与右值|左值引用与右值引用
- 1. 为什么要学习左值与右值?
- 2.左值和右值的概念
- 2.1 什么是左值?
- 2.1.1 常见的左值
- 2.3 什么是右值?
- 2.3.1 常见的纯右值
- 2.2.2 什么是将亡值
- 2.2.3 产生将亡值的情形
1. 为什么要学习左值与右值?
C++当中的值语义:
GC(Garbage Collection, 垃圾回收)语言之中, 大部分变量都是引用语义, 内存管理交给GC. 通过值语义, 能够方便直观地控制对象的生命周期,让RAII(Resource Acquisition Is Initialization, 即通过构造和析构函数来获取和释放资源,管理变量的生命周期)用起来更自然.
C++ 11中引入了右值引用的概念, 实现移动语义和完美转发, 提高程序的性能和效率. 这也成为C++相关岗位面试中的一大热点, 因此很有必要掌握左值与右值的概念, 以及左值引用和右值引用的相关知识.
2.左值和右值的概念
2.1 什么是左值?
—— 简单地说, 它是指向内存位置的表达式,其值可以被修改, 它的特点是:
-
- 可以出在等号左边;
-
- 能够取地址
-
- 具有别名
2.1.1 常见的左值
- 变量名
int x = 5;
- 返回左值引用的函数调
// 返回变量 x 的引用
int& getX() {return x;
}
- 前置自增自减
int y = ++x; // 预增操作,x 先自增再赋值给 y, 这里的++x返回的就是一个左值
++x = 10; // ++x, 自增操作返回一个左值,可以用10对其赋值
- 赋值运算或复合赋值运算
++x = 10; // 自增操作返回左值,可以赋值
std::cout << "x: " << x << std::endl; // 输出 10++x += 3; // 自增操作返回左值,可以进行复合赋值std::cout << "x: " << x << std::endl; // 输出 9
- 解引用
++*ptr = 10; // 自增指针解引用返回左值,可以赋值
2.3 什么是右值?
-
- 不能被赋值的表达式,通常是临时值或字面常量
-
右值分为纯右值与将亡值
2.3.1 常见的纯右值
- 字面值——字面值有整型、浮点数、字符、字符串、布尔型、空指针等, 其中除了字符串型的字面值以外, 其他均为右值.
int a=8; //这里的8就是一个字面值double b = 3.14; // 3.14 是右值char c = 'A'; // 'A' 是右值bool d = true; // true 是右值int* ptr = nullptr; // nullptr 是右值const char* str = "Hello"; // "Hello" 是一个指向常量字符数组的左值
- 非引用类型的函数调用
int add(int a, int b) {return a + b;
}// 不能对右值进行赋值
// add(x, y) = 20; // 错误
- 后置自增自减
int a = 5;int b = a++; // a++ 返回右值,b 的值是 5,a 的值变为 6std::cout << "a: " << a << ", b: " << b << std::endl; // 输出 a: 6, b: 5// 不能对右值进行赋值// a++ = 10; // 错误
- 算术表达式
int a = 10, b = 20;int c = a + b; // a + b 返回右值std::cout << "c: " << c << std::endl; // 输出 30// 不能对右值进行赋值// (a + b) = 50; // 错误
- 逻辑表达式
bool x = true, y = false;bool z = x && y; // x && y 返回右值std::cout << "z: " << z << std::endl; // 输出 0 (false)// 不能对右值进行赋值// (x && y) = true; // 错误
- 比较表达式
int a = 5, b = 10;bool result = a < b; // a < b 返回右值, 因为a<b是不能被赋值的// (a < b) = false; //这种写法错误
2.2.2 什么是将亡值
它表示资源即将被移动或者销毁的对象
2.2.3 产生将亡值的情形
- std::move 函数的返回值——因为这个函数调用移动赋值运算符而不是拷贝赋值运算符。例如下例:
#include <iostream>
#include <utility> // std::move
#include <string>class MyClass {
public:MyClass() : data(new int[100]) {std::cout << "Default constructor" << std::endl;}~MyClass() {delete[] data;std::cout << "Destructor" << std::endl;}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = nullptr;std::cout << "Move constructor" << std::endl;}// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {if (this != &other) {delete[] data;data = other.data;other.data = nullptr;std::cout << "Move assignment operator" << std::endl;}return *this;}private:int* data;
};int main() {//...省略部分obj1的定义MyClass obj2;obj2 = std::move(obj1); // 这里触发了移动赋值运算符return 0;
}
- 临时对象(尤其是那些通过std::move显式转换的临时对象), 例如:
MyClass createObject() {MyClass obj;return obj; // 返回值优化 (RVO) 会避免多次拷贝,这里的 obj 是一个将亡值
}3. 返回右值引用的函数。