【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;
1
返回左值引用的函数调
// 返回变量 x 的引用
int& getX() {
return x;
}
前置自增自减
int y = ++x; // 预增操作,x 先自增再赋值给 y, 这里的++x返回的就是一个左值
++x = 10; // ++x, 自增操作返回一个左值,可以用10对其赋值
1
2
赋值运算或复合赋值运算
++x = 10; // 自增操作返回左值,可以赋值
std::cout << "x: " << x << std::endl; // 输出 10
++x += 3; // 自增操作返回左值,可以进行复合赋值
std::cout << "x: " << x << std::endl; // 输出 9
解引用
++*ptr = 10; // 自增指针解引用返回左值,可以赋值
1
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 的值变为 6
std::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. 返回右值引用的函数。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/steptoward/article/details/139568337