又学懂辣 !五分钟带你掌握C++中的左值,右值,左值引用,右值引用 以及++i和i++的深度刨析 史上最详细!

1. 左值

定义:

一句话概括,左值是指可以获取地址且具有持久性的对象或表达式,通常出现在'='的左边

#include <iostream>

int main(void) {
    int a = 66;       // 变量 a 是一个左值
    int b = a;        // a 是左值,可以赋值给另一个左值 b
    int* p = &a;      // 可以获取 a 的地址

    std::cout << "Value of a: " << a << std::endl;    // 输出 42
    std::cout << "Address of a: " << p << std::endl;  // 输出 a 的地址

    *p = 88;          // 修改 p 指向的地址的值,即修改 a 的值
    std::cout << "New value of a: " << a << std::endl; // 输出 50

}
  • a 是一个左值,它有一个固定的内存地址,可以通过 &a 获取。
  • 我们可以将 a 的值赋给 b,并且可以修改 a 的值。

2. 右值

定义:

一句话概括,右值是指那些临时性、无法被取地址的对象或表达式,只能出现在'='的右边

#include <iostream>

int main(void) {
    int x = 10;
    int y = x + 5;    // 表达式 x + 5 是一个右值
                      // 它的结果只能短暂存在于 y 被赋值的瞬间

    std::cout << "Value of x: " << x << std::endl; // 输出 10
    std::cout << "Value of y: " << y << std::endl; // 输出 15

    // 尝试获取右值的地址是非法的
    // int* p = &(x + 5); // 错误:不能获取右值的地址

    // 右值只能在其生命周期内使用,之后就被销毁

}
  • 表达式 x + 5 是一个右值,因为它是一个临时计算的结果。一旦 y 被赋值,该右值就不存在了。
  • 右值不能获取地址,所以 int* p = &(x + 5); 是非法的。

3. 左值引用

定义:

一句话概括,左值引用是对已存在变量对象取的别名,允许我们对这个对象进行操作而不需要复制 用&来声明

左值引用的具体用法如下:

1.传递函数参数时避免复制:void printValue(const std::string& str);
2.返回内部对象的引用:std::string& getValue();
3.延长对象生命周期:在函数内部使用局部变量的引用。

#include <iostream>
#include <string>

// 函数声明:避免复制,通过引用传递参数
void printValue(const std::string& str) {
    std::cout << "String value: " << str << std::endl;
}

// 类声明:返回内部对象的引用,避免不必要的复制
class Myclass {
public:
    Myclass(const std::string& s) : value(s) {}
    const std::string& getValue() const { return value; } // 返回内部对象的引用

private:
    std::string value;
};

int main(void) {

    int a=66;
    int &b=a;//给a取别名
    b=88;
    std::cout<<b<<std::endl;//打印88

    std::string myString = "Hello, World!";
    
    // 传递参数时避免复制
    printValue(myString);

    Myclass myclass("test");
    const std::string& ref = mycalss.getValue(); // 返回内部对象的引用
    std::cout<< ref << std::endl; // 使用对象的引用延长生命周期

}

4. 右值引用

定义:

一句话概括,右值引用就是一个右值的引用,通常用于移动语义优化临时对象的资源管理,用&&来声明

右值引用的引用场景如下:

1.实现移动语义,通过转移资源来提高性能
2.重载函数,通过区分左值和右值处理提高性能

#include <iostream>
#include <string>
#include <utility> // for std::move

// 实现移动语义
class Myclass {
public:

    // 移动构造函数
    Myclass(Myclass&& other) noexcept : data(std::move(other.data)) {
        other.data.clear(); // 迁移资源后清空源对象
    }

    // 移动赋值操作符
    Myclass& operator=(Myclass&& other) noexcept {
        if (this != &other) {//避免自赋值
            std::cout << data << other.data << std::endl;
            data = std::move(other.data);
            other.data.clear();
        }
        return *this;
    }
private:
    std::string data;
};

// 重载函数,区分左值和右值处理
void printMyclass(Myclass& obj) {
    std::cout << "lvalue: " << obj.data << std::endl;
}

void printMyclass(Myclass&& obj) {
    std::cout << "rvalue: " << obj.data << std::endl;
}

int main() {

    int &&a=666;//可以捕获右值
    std::cout<<a<<std::endl;

    Myclass obj1("Hello peng_ge!");
    
    // 通过左值引用调用
    printMyclass(obj1); // 处理左值

    // 通过右值引用调用
    printMyclass(std::move(obj1)); // 处理右值,资源被转移

    Myclass obj2("Hello xuan_mei!");
    Myclass obj3 = std::move(obj2); // 使用移动构造函数,避免复制

    Myclass obj4("Hello ccsu!");
    obj4 = std::move(obj3); // 使用移动赋值操作符,避免复制

    return 0;
}

5.i++和++i的深度刨析

关于i++和++i谁是左值谁是右值是在面试中经常会问到的问题 如果你还分辨不清 那就太对辣!直接往下看,使你收获颇丰!

i++和++i的返回值性质

i++执行过程:

1.保存当前的 i 值到一个临时变量。

2.对 i 进行递增操作(即 i = i + 1)。

3.返回保存的临时变量的值。

i++返回值:

1.返回的是一个临时值,也就是 i 递增前的值。

2.这个临时值是一个右值,它存在于表达式的上下文中,只在该表达式的生命周期内有效。

3.由于它是临时的右值,不能作为左值来使用(即不能直接给它赋值)。

++i执行过程:

1.对 i 进行递增操作(即 i = i + 1)。

2.返回递增后的 i 本身(即递增后的 i 的引用)。

++i的返回值:

1.返回的是 i 递增后的新值,实际上是 i 这个变量本身。

2.由于返回的是 i 本身(一个左值),它可以在后续的操作中继续使用。

综上所述:

i++为右值

++i为左值

i++和++i的内存管理方面的差异

用类来模拟

class Counter {
public:
    Counter() : value(0) {}
    Counter& operator++() { // 前置递增++i
        ++value;
        return *this;
    }
    Counter operator++(int) { // 后置递增i++
        Counter temp = *this; // 创建临时变量
        ++value;
        return temp; // 返回临时变量
    }
private:
    int value;
};

在上面的类定义中:

    前置递增 ++i 直接递增 value 并返回对象的引用,没有额外的开销。

    后置递增 i++ 创建了一个 Counter 的副本,递增原对象的值,然后返回副本,涉及额外的内存分配和复制操作

i++和++i总结

   1.i++ 返回的是临时值(右值),涉及创建和管理临时变量。

   2.++i 返回的是变量 i 本身(左值),直接操作和返回变量,没有额外的临时变量开销。

         3.在实际使用中,++i 通常比 i++ 更高效,所以能用++i就尽量不用i++。

总结

1.左值是具有持久性可以获取地址的对象或表达式,通常出现在赋值操作符的左边,例如变量或具名对象。

2右值临时性的、无法获取地址的对象或表达式,通常出现在赋值操作符的右边,例如临时计算的结果或字面量。

3.左值引用使用 & 声明,用于操作现有对象而避免复制,如传递参数和返回内部对象引用。

4.右值引用使用 && 声明,用于实现移动语义和优化临时对象的资源管理。

  • 41
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值