左值与右值 in C++【C++学习笔记】

85.左值与右值 in C++

🍅左值和右值是什么

现在又很多人称左值为有地址的值,因为它们是有地址的

如果要给一个具体的定义

  • 左值是由某种存储支持的变量
  • 右值是临时值
int i = 10;

如上表达式有两部分,左边和右边

  • 左值绝大多数时候在等号左边,右值在右边(但这并不总是适用的!)
  • 可以将右值赋值给左值,但不能给一个右值赋值(比如给10赋值)
int i = 10;
int a = i;

在这里等号左边是左值,右边同样是左值,所以才说等号右边就是右值的说法是错的❌

右值不仅仅是字面量,它还可以是函数的结果👇

#include <iostream>

int GetValue() {
    return 10;
}

int main() {
    int i = GetValue();
}

在这个例子中,函数返回了一个右值,一个临时值

而如果函数返回的是左值,那就变得有趣了,我们可以通过返回int &来实现,而这就叫做左值引用

🍅左值和左值引用

#include <iostream>

int &GetValue() {
    static int value = 10;	//提供了储存空间
    return value;
}

int main() {
    GetValue = 5;	//这样做是可行的!!!
}

再展开说一下,如果我有一个函数,它有一个值,那么可以用很多方法去调用这个函数。我可以用左值或者右值来调用它

#include <iostream>

void SetValue(int value) {		//什么都没有
}

int main() {
    int i = 10;
    SetValue(i);	//这里的参数便是一个左值
    SetValue(10);	//这里的参数便是一个右值(临时变量右值)
}

如上可以马上看出来哪个是临时的,哪个不是,因为有一个规则是你不能将右值赋给左值引用,所以左值引用的只能是左值

  • void SetValue(int &value) {}
    SetValue(10);	//❌❌❌报错,这里的形参必须是一个左值
    

上面的操作其实有个特殊的规则,它是一种变通的方法

  • int &a = 10;	//同上,是错的❌
    const int &b = 10;	//这个是对的✅
    /*	真实情况
    int temp = 10;
    const int &b = temp;
    */
    

    编译器会创建一个临时变量,然后把它赋值给那个引用。故这个操作其实不可避免的创建了一个左值,使得它能同时支持了左值和右值

    故其实**const左值引用**实际上可以同时接受左值和右值

再谈字符串的例子

std::string a = "123";
std::string b = "456";
std::string c = a + b;		//a + b是一个临时变量!

如上式中,等号左边的皆是左值,右边的皆是右值。这里比较迷的是a + b是右值。

  • a和b组成了一个临时字符串,然后把它赋值给了一个左值

故在下面的例子便可展示这点

void PrintStr(string &str) {
    std::cout << str << "\n";
}
std::string a = "123";
std::string b = "456";
PrintStr(a + b);	//❌这是错的,因为这是个右值
/*
void PrintStr(const string &str) {	//如果这里加一个const,那么就对了
    std::cout << str << "\n";
}
*/

所以这便是为什么很多用C++写的常量引用。因为它们兼容临时的右值和实际存在的左值变量!

所以这里就有一个很简单的办法检验一个量是左值还是右值:可以写一个非常量的左值引用,因为左值引用只能接受左值

那有没有办法写一个函数,只接受临时对象呢?当然有,因此这里要用到右值引用

🍅右值和右值引用

右值引用看起来和左值引用差不多,不过右值引用需要两个&&

void PrintStr(string &&str) {	//注意这里两个&
    std::cout << str << "\n";
}
std::string a = "123";
std::string b = "456";
std::string c = a + b;
PrintStr(c);	//❌这是右值引用
PrintStr(a + b);	//✅

右值引用中,不能传递左值,但可以传递右值

确实有点猛,但是这玩意有什么用呢?答案是:它非常有用,尤其是在移动语义方面

这里的主要优势在于优化

  • 如果我们知道传入的是一个临时对象的话,那么我们就不需要担心它们是否活着,是否完整,是否拷贝。我们可以简单地偷它的资源,给到特定的对象,或者其他地方使用它们。因为我们知道它是暂时的,它不会存在很长时间
  • 而如果如上使用const string& str,虽然可以兼容右值,但是却不能从这个字符串中窃取任何东西!因为这个str可能会在很多函数中使用,不可乱修改!(所以才加了const)
  • 而在PrintStr(a + b)这里显然是暂时的,这个右值只会在这个特定的PrintStr()调用中被使用

最后回顾定义

  • 左值是由某种存储支持的变量左值有地址和值,可以出现在赋值运算符左边或者右边。
    • 左值引用仅仅接受左值,除非用了const兼容
  • 右值是临时值右值只有值,只能出现在赋值运算符右边。
    • 右值只有值,没有地址, 右值是一个优化技巧(C++),因为右值往往是临时变量的。
    • 右值引用仅仅接受右值

所以左值和右值的差别便在于有没有地址这一说!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值