【5minC++基本功】——左值与右值|左值引用与右值引用

发布于:2024-06-11 ⋅ 阅读:(38) ⋅ 点赞:(0)

1. 为什么要学习左值与右值?

C++当中的值语义:
GC(Garbage Collection, 垃圾回收)语言之中, 大部分变量都是引用语义, 内存管理交给GC. 通过值语义, 能够方便直观地控制对象的生命周期,让RAII(Resource Acquisition Is Initialization, 即通过构造和析构函数来获取和释放资源,管理变量的生命周期)用起来更自然.

C++ 11中引入了右值引用的概念, 实现移动语义完美转发, 提高程序的性能和效率. 这也成为C++相关岗位面试中的一大热点, 因此很有必要掌握左值与右值的概念, 以及左值引用和右值引用的相关知识.

2.左值和右值的概念

2.1 什么是左值?

—— 简单地说, 它是指向内存位置的表达式,其值可以被修改, 它的特点是:

    • 可以出在等号左边;
    • 能够取地址
    • 具有别名

2.1.1 常见的左值

  1. 变量名
int x = 5;        
  1. 返回左值引用的函数调
// 返回变量 x 的引用
int& getX() {
    return x;
}
  1. 前置自增自减
int y = ++x; // 预增操作,x 先自增再赋值给 y, 这里的++x返回的就是一个左值
++x = 10; // ++x, 自增操作返回一个左值,可以用10对其赋值
  1. 赋值运算或复合赋值运算
++x = 10; // 自增操作返回左值,可以赋值
std::cout << "x: " << x << std::endl; // 输出 10

++x += 3; // 自增操作返回左值,可以进行复合赋值
    std::cout << "x: " << x << std::endl; // 输出 9


  1. 解引用
++*ptr = 10; // 自增指针解引用返回左值,可以赋值

2.3 什么是右值?

    • 不能被赋值的表达式,通常是临时值或字面常量
  • 右值分为纯右值与将亡值

2.3.1 常见的纯右值

  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" 是一个指向常量字符数组的左值
  1. 非引用类型的函数调用
int add(int a, int b) {
    return a + b;
}

// 不能对右值进行赋值
// add(x, y) = 20; // 错误
  1. 后置自增自减
    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; // 错误
  1. 算术表达式
    int a = 10, b = 20;
    int c = a + b; // a + b 返回右值
    std::cout << "c: " << c << std::endl; // 输出 30

    // 不能对右值进行赋值
    // (a + b) = 50; // 错误

  1. 逻辑表达式
    bool x = true, y = false;
    bool z = x && y; // x && y 返回右值
    std::cout << "z: " << z << std::endl; // 输出 0 (false)

    // 不能对右值进行赋值
    // (x && y) = true; // 错误

  1. 比较表达式
    int a = 5, b = 10;
    bool result = a < b; // a < b 返回右值, 因为a<b是不能被赋值的
    
    // (a < b) = false; //这种写法错误

2.2.2 什么是将亡值

它表示资源即将被移动或者销毁的对象

2.2.3 产生将亡值的情形

  1. 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;
}
  1. 临时对象(尤其是那些通过std::move显式转换的临时对象), 例如:
MyClass createObject() {
    MyClass obj;
    return obj; // 返回值优化 (RVO) 会避免多次拷贝,这里的 obj 是一个将亡值
}

3. 返回右值引用的函数。

网站公告

今日签到

点亮在社区的每一天
去签到