c++之说_14|左值引用与右值引用

提起左右值引用我就头疼

左值:

1、在内存中开辟了空间的便叫左值

2、左值不一定可以赋值  如字符串常量

3、左值可以取地址

右值:

1、在内存中没有开辟空间的

2、右值无法取地址

如:

立即数(1,2,3)

函数返回值 直接返回非类型引用

Lvalue getT() { return Lvalue(); };
int get() { return c;  };

返回的这个int值或Lvalue类型对象就是一个右值 因为没有开辟空间

3、将亡值

什么叫将亡值  我的理解是一种标志 告诉说这个对象我要销毁

Lvalue&& getR();  函数返回值是右值引用 返回的这个对象 就叫将亡对象  即将销毁Lvalue c;
static_cast<Lvalue&&>(c);  转换返回的操作数也叫将亡值

我的理解就是告诉我们 这个值是要销毁的  推荐使用一个新对象去存储

  什么叫左值引用? 其实就是 这个对象所代表的内存空间的另一个名字  在编译器内部

  本质上是指针实现的

   对左值引用取地址  所取的是引用对象的地址值

Lvalue d;//左值const Lvalue& b2 = d;//绑定左值
Lvalue& b3 = d;//绑定左值&b2 == &d;
&b3 == &d;

而右值引用 则引用的是右值

Rvalue r;//左值
Rvalue&& r1 = Rvalue();//绑定右值//r1 是左值  有名的右值引用  为 左值
const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)成将亡值Rvalue&& r3 = static_cast<Rvalue&&>(r);

记住有名字的右值引用  是左值  所以可以取地址

std::cout << "r:" << &r << endl;
std::cout << "r1:" << &r1 << endl;
std::cout << "r2:" << &r2 << endl;
std::cout << "r3:" << &r3 << endl;&r == &r3;
&r1 == &r2;

由于是引用  当然可以改变所引用的对象

struct Rvalue
{};
struct Lvalue : public Rvalue
{int c;Lvalue() = default;Lvalue(int Inc) :c(Inc) {};Lvalue getT() { return Lvalue(); };Lvalue&& getR() { auto c = new Lvalue(100);  return static_cast<Lvalue&&>(*c);};int get() { return c;  };
};
std::cout << "" << endl;
Lvalue op(50);
std::cout << "op.c:" << op.c << endl;
Lvalue& Lop = op;
Lop.c = 100;
std::cout << "左值引用修改"<< endl;
std::cout << "op.c:" << op.c << endl;
Lvalue& Rop = op;
Lop.c = 800;
std::cout << "右值引用修改" << endl;
std::cout << "op.c:" << op.c << endl;

这么一看左右值引用都是一样的感觉

他们主要体现在函数上

struct Lvalue : public Rvalue
{int c;int* p;Lvalue() = default;Lvalue(int Inc) :c(Inc), p(new int()) {};Lvalue& operator=(Lvalue& lc) { c = lc.c;p = new int;*p = * lc.p;return *this; };Lvalue& operator=(const Lvalue& lc) {c = lc.c;p = new int;*p = *lc.p;return *this; };Lvalue& operator=(Lvalue&& lc) {c = lc.c;p = lc.p;lc.p = nullptr;return *this;};Lvalue& operator=(const Lvalue&& lc) {	Lvalue&& Rc = const_cast<Lvalue&&> (lc);c = Rc.c;p = Rc.p;Rc.p = nullptr;return *this; };Lvalue getT() { return Lvalue(); };Lvalue&& getR() { auto c = new Lvalue(100);  return static_cast<Lvalue&&>(*c);};int get() { return c;  };
};

注意那几个 operator= 重载赋值运算符的函数

这其实就是所谓的 深拷贝  浅拷贝

//深拷贝
Lvalue& operator=(Lvalue& lc) 
{ c = lc.c;p = new int;    //重新开辟了堆空间 *p = * lc.p;    //将 lc.p 中的int值 存入新开的堆空间中return *this; 
};//浅拷贝
Lvalue& operator=(Lvalue&& lc) 
{c = lc.c;p = lc.p;        
//没有开辟新的堆空间  直接拿到 将亡值 lc 的 p所存储的内存地址  达到复用的目的
// 这也是他们所说的窃取资源lc.p = nullptr;return *this;
};
Lvalue& operator=(Lvalue& lc) 
{ std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;c = lc.c;delete p;p = new int;*p = * lc.p;return *this; 
};
Lvalue& operator=(const Lvalue& lc) 
{std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;c = lc.c;delete p;p = new int;*p = *lc.p;return *this; 
};
Lvalue& operator=(Lvalue&& lc) 
{std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;c = lc.c;delete p;p = lc.p;lc.p = nullptr;return *this;
};
Lvalue& operator=(const Lvalue&& lc) 
{	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;Lvalue&& Rc = const_cast<Lvalue&&> (lc);c = Rc.c;delete p;p = Rc.p;Rc.p = nullptr;return *this; 
};

调用处

Lvalue t1(100);
*t1.p = 80;
Lvalue t2(200);
*t2.p = 800;
std::cout << "" << endl;
std::cout << "初始化时" << endl;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;std::cout << "" << endl;
std::cout << "t1 = t2" << endl;t1 = t2;//Lvalue& operator=(Lvalue& lc) ;std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;std::cout << "" << endl;
std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;t1 = static_cast<Lvalue&&>(t2); 
//Lvalue& operator=(Lvalue&& lc)  如果没有  就考虑 Lvalue& operator=(const Lvalue&& lc)  
如果还没有 就调用Lvalue& operator=(const Lvalue& lc) std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的

我们注释掉

/* Lvalue& operator=(Lvalue&& lc) 
{std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;c = lc.c;delete p;p = lc.p;lc.p = nullptr;return *this;
}; */Lvalue& operator=(const Lvalue&& lc) 
{	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;Lvalue&& Rc = const_cast<Lvalue&&> (lc);c = Rc.c;delete p;p = Rc.p;Rc.p = nullptr;return *this; 
};

看调用 Lvalue& operator=(const Lvalue&& lc) 

我们继续注释

/* Lvalue& operator=(Lvalue&& lc) 
{std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;c = lc.c;delete p;p = lc.p;lc.p = nullptr;return *this;
}; *//*
Lvalue& operator=(const Lvalue&& lc) 
{	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;Lvalue&& Rc = const_cast<Lvalue&&> (lc);c = Rc.c;delete p;p = Rc.p;Rc.p = nullptr;return *this; 
};
*/

看调用与结果

这就是右值引用存在的价值

告诉大家这个对象需要销毁  你怎么处理看你的了  

如果只用左值引用 无法很好的区分  因为cosnt lvalue&&  即可左值也可右值

你不知道传进来的这个对象是 const 修饰的对象  还是  要表示的销毁对象

我们也看到了引用是可以直接修改对象的  所以也是会有点问题  有些人不按照规则来

这样的函数 是有隐患的  我的本意是  这个返回值是即将销毁的值  然而外部可修改

Lvalue&& getR() { auto c = new Lvalue(100);  return static_cast<Lvalue&&>(*c);
};

折叠引用  使用类型别名  using  typedef  即可达到折叠引用的目的

using ob = Lvalue&;
typedef Lvalue& ob1;
using ou = Lvalue&&;
typedef Lvalue&& ou1;Lvalue k;
ob& o = k;//左值引用
ob && o1= k;//左值引用
ou& o2 = k;//左值引用
ou&& o3 = (Lvalue&&)k;//右值引用

折叠引用只有一个规则:

右值引用的右值引用 才是右值引用  其余都为左值引用

&& &&  右值引用

& && 左值引用

&& & 左值引用

& & 左值引用

而我们所知晓的 std::forward 完美转发  返回值就是  &&

用了点折叠引用

好了  到最后了 

我们说一下

空类 和 继承 一个空类 内存大小是什么样的

Lvalue d;//左值
Rvalue r;//左值
std::cout<<	sizeof(d)<<endl;//4   继承一个空类  空类所占的1字节被优化掉
std::cout << sizeof(r) << endl;//1

  写完了

对了知乎大佬的参考  左右值引用的要点  

(3 条消息) c++为什么要搞个引用岀来,特别是右值引用,感觉破坏了语法的简洁和条理,拷贝一个指针不是很好吗? - 知乎 (zhihu.com)

最后参考代码


struct Rvalue
{};
struct Lvalue : public Rvalue
{int c;int* p;Lvalue() = default;Lvalue(int Inc) :c(Inc), p(new int()) {};Lvalue& operator=(Lvalue& lc) { std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;c = lc.c;delete p;p = new int;*p = * lc.p;return *this; };Lvalue& operator=(const Lvalue& lc) {std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;c = lc.c;delete p;p = new int;*p = *lc.p;return *this; };/*Lvalue& operator=(Lvalue&& lc) {std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;c = lc.c;delete p;p = lc.p;lc.p = nullptr;return *this;};*//*Lvalue& operator=(const Lvalue&& lc) {	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;Lvalue&& Rc = const_cast<Lvalue&&> (lc);c = Rc.c;delete p;p = Rc.p;Rc.p = nullptr;return *this; };*/Lvalue getT() { return Lvalue(); };Lvalue&& getR() { auto c = new Lvalue(100);  return static_cast<Lvalue&&>(*c);};int get() { return c;  };
};int main(int line,   const char* arg[])
{Lvalue d;//左值Rvalue r;//左值std::cout<<	sizeof(d)<<endl;//4   继承一个空类  空类所占的1字节被优化掉std::cout << sizeof(r) << endl;//1//const 修饰的左值引用 既可以绑定左值又可以绑定右值     Lvalue() 右值  无名的const Lvalue& b = Lvalue();//绑定右值const Lvalue& b2 = d;//绑定左值Lvalue& b3 = d;//绑定左值Rvalue&& r1 = Rvalue();//绑定右值//r1 是左值  有名的右值引用  为 左值const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)Rvalue&& r3 = static_cast<Rvalue&&>(r);//std::move(r);std::cout << "左值"<< endl;std::cout << "d:"<< & d << endl;std::cout << "b2:"<< &b2 << endl;std::cout << "b:" << &b << endl;std::cout << "b3:" << &b3 << endl;std::cout << "右值"<< endl;std::cout << "r:" << &r << endl;std::cout << "r1:" << &r1 << endl;std::cout << "r2:" << &r2 << endl;std::cout << "r3:" << &r3 << endl;//td::cout << "纯右值" << &Rvalue() << endl; //error//&d.getT();decltype(auto) v = d.getR();v.c = 50;std::cout << "v:" << &v << endl;auto str = "dadad";std::cout << "字符串:" << &"dadad" << endl;std::cout << "str:" << &str << endl;std::cout << "" << endl;Lvalue op(50);std::cout << "op.c:" << op.c << endl;Lvalue& Lop = op;Lop.c = 100;std::cout << "左值引用修改"<< endl;std::cout << "op.c:" << op.c << endl;Lvalue& Rop = op;Lop.c = 800;std::cout << "右值引用修改" << endl;std::cout << "op.c:" << op.c << endl;int&& i = 10;i = 80;Lvalue t1(100);*t1.p = 80;Lvalue t2(200);*t2.p = 800;std::cout << "" << endl;std::cout << "初始化时" << endl;std::cout << "t1.c:" << t1.c << endl;std::cout << "t1.p:" << t1.p << endl;std::cout << "*t1.p:" << *t1.p << endl;std::cout << "" << endl;std::cout << "t2.c:" << t2.c << endl;std::cout << "t12.p:" << t2.p << endl;std::cout << "*t2.p:" << *t2.p << endl;std::cout << "" << endl;std::cout << "t1 = t2" << endl;t1 = t2;//Lvalue& operator=(Lvalue& lc) ;std::cout << "t1.c:" << t1.c << endl;std::cout << "t1.p:" << t1.p << endl;std::cout << "*t1.p:" << *t1.p << endl;std::cout << "" << endl;std::cout << "t2.c:" << t2.c << endl;std::cout << "t12.p:" << t2.p << endl;std::cout << "*t2.p:" << *t2.p << endl;std::cout << "" << endl;std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;t1 = static_cast<Lvalue&&>(t2); //Lvalue& operator=(Lvalue&& lc)  如果没有  就考虑 Lvalue& operator=(const Lvalue&& lc)  如果还没有 就调用Lvalue& operator=(const Lvalue& lc) std::cout << "t1.c:" << t1.c << endl;std::cout << "t1.p:" << t1.p << endl;std::cout << "*t1.p:" << *t1.p << endl;std::cout << "" << endl;std::cout << "t2.c:" << t2.c << endl;std::cout << "t12.p:" << t2.p << endl;//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的using ob = Lvalue&;typedef Lvalue& ob1;using ou = Lvalue&&;typedef Lvalue&& ou1;Lvalue k;ob& o = k;//左值引用ob && o1= k;//左值引用ou& o2 = k;//左值引用ou&& o3 = (Lvalue&&)k;//右值引用std::forward<Lvalue&&>(k);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/464447.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前后端分离nodejs+vue动态网站的图书借阅管理系统35ih5

读者模块 1)注册&#xff1a;读者输入账号、密码、确认密码、姓名、手机、身份证、邮箱&#xff0c;点击注册按钮&#xff0c;完成注册。 2)登录&#xff1a;普通读者成功输入读者账号和密码&#xff0c;点击登录按钮。 3)读者主页面&#xff1a;读者登录成功后&#xff0c;选择…

React - 分页插件默认是英文怎么办

英文组件的通用解决方案 这里以分页插件为例&#xff1a; 大家可以看到&#xff0c;最后的这个页面跳转提示文字为Go to&#xff0c;不是中文&#xff0c;而官网里面的案例则是&#xff1a; 解决方案&#xff1a; import { ConfigProvider } from antd; import zhCN from an…

【Linux】信号概念与信号产生

信号概念与信号产生 一、初识信号1. 信号概念2. 前台进程和后台进程3. 认识信号4. 技术应用角度的信号 二、信号的产生1. 键盘组合键2. kill 命令3. 系统调用4. 异常&#xff08;1&#xff09;观察现象&#xff08;2&#xff09;理解本质 5. 软件条件闹钟 一、初识信号 1. 信号…

【MySQL】MySQL表的增删改查(进阶)

MySQL表的增删改查&#xff08;进阶&#xff09; 1. 数据库约束1.1 约束类型1.2 NULL约束1.3 UNIQUE:唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束1.6 FOREIGN KEY&#xff1a;外键约束:1.7 CHECK约束&#xff08;了解&#xff09; 2. 表的设…

python健身房管理系统 django健身课程预约系统

系统所要实现的功能分析&#xff0c;对于现在网络方便的管理&#xff0c;系统要实现用户可以直接在平台上进行查看首页、健身课程、留言板、个人中心、后台管理等&#xff0c;根据自己的需求可以进行查看健身课程&#xff0c;这样既能节省用户的时间&#xff0c;不用在像传统的…

H12-821_35

35.如图所示,SWA、SWB、SWC都运行RSTP,SWB上的GEO/O/2端口和SWC上的GEO/0/1端其端口角色为? A.backup端口.Alternative端口 B.Alternative端口、Backup端口 C.Root端口、Designate端口 D.Backup端口、Root端口 答案&#xff1a;A 注释&#xff1a; 一个链路&#xff08;冲突域…

Bean 的生命周期

Bean 的生命周期 ⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期. Bean 的⽣命周期分为以下 5 个部分: 1. 实例化(为Bean分配内存空间) 2. 属性赋值( Bean 注⼊和装配,⽐如 AutoWired ) 3. 初始化 a. 执⾏各种通知,如 BeanNameA…

Java:Arrays类、Lambda表达式、JDK新特性(方法引用) --黑马笔记

一、Arrays类 1.1 Arrays基本使用 Arrays是操作数组的工具类&#xff0c;它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。 下面我们用代码来演示一下&#xff1a;遍历、拷贝、排序等操作。需要用到的方法如下&#xff1a; public class ArraysTest1 {public stat…

036 冒泡排序

代码实践 // 冒泡排序 static void bubbleSort(Comparable[] elements) {// 临时容器&#xff0c;用于变量交换值时存储Object temp;// 标志位 用于减少无意义的循环次数boolean flag;for (int i 0; i < elements.length - 1; i) {flag false;for (int j 0; j < elem…

低代码开发的数字化革新:实业界的成功秘诀与实践之路

在信息时代&#xff0c;实业界正迎来一场变革的风暴&#xff0c;传统的管理系统逐渐不再满足越来越复杂的生产流程和不断增长的市场需求&#xff0c;而低代码开发正如一颗璀璨的明星&#xff0c;将为企业带来前所未有的灵活性和创新力&#xff0c;这并非一场技术的简单变迁&…

洗地机买什么品牌好?最好的洗地机品牌

对于双职工家庭来说&#xff0c;日常家务活是一个很大的难题。所以就要借助洗地机来提升生活质量&#xff0c;确实智能家电能够帮助日常家务减负。市面上的洗地机的品牌是真的很多&#xff0c;笔者今天带大家一起来看看什么洗地机品牌好用。 该如何挑选适合自己的家用洗地机 …

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之AlphabetIndexer组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之AlphabetIndexer组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、AlphabetIndexer组件 可以与容器组件联动用于按逻辑结构快速定位容器显…