【c++11】 左值引用和右值引用

news2024/10/6 8:26:39

c+11特性

  • 右值引用
    • 左值引用和右值引用
      • 左值引用
      • 右值引用
      • 比较
    • 右值引用的应用
      • 左值引用的短处
      • 右值引用解决问题
        • 移动构造
    • STL的改动
    • move()函数
    • 结语

右值引用

c++从出现就有着引用的语法,但是在c++11后又新增了右值引用的新特性,以往所学的引用成了左值引用。非左即右
无论是左值引用还是右值引用,都是给对象取别名

左值引用和右值引用

左值引用

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

//a,c,p都是左值
int a=0;
const int c=5;
int *p=&a;

//ref都是对以上左值的引用
int& ref1=a;
int& ref2=c;
int*& ref3=p;

右值引用

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,**右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。**右值引用就是对右值的引用,给右值取别名。

int x=10;int y=20;

//以下都是常见的右值
10;
x+y;
min(x,y)//函数返回值

//以下是右值引用
int&& ref1=10;
int&& ref2=x+y;
int&& ref3=min(x+y);

区分左值和右值最常见的方法就是能不能取地址

int a=10;
int *p=&a;//左值
//错误的
int *p1=&10;//右值无法取地址

比较

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。

右值引用总结:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
int a=10;
//左值引用引用左值
int &ref1=a;
//左值引用引用右值
const int& ref2=10;
//右值引用引用右值
int&& ref3=10;
//右值引用引用左值
int&& ref4=std::move(a)//右值引用可以引用move以后的左值,不能直接引用

右值引用的应用

右值引用一般用于自定义类型,右值又分为纯右值 (prvalue)、将亡值 (xvalue)

xvalue(eXpiring value)字面意思可理解为生命周期即将结束的值(将亡值),它是某些涉及到右值引用的表达式的值,左值也可以是将亡值,例如:调用一个返回类型为右值引用的函数的返回值就是xvalue。

prvalue(pure rvalue)字面意思可理解为纯右值,传统意义上的右值,例如临时对象和字面值常量(字符串字面值除外)等。

左值引用的短处

当我们在写一个函数解决某些需求时,返回值通常是需要返回函数栈帧里创建的临时变量,因为是临时变量,只能进行传值返回,但是当函数执行结束后,函数栈帧会自动销毁,函数栈帧里的局部变量也会销毁,如果我们要返回函数栈帧里创建的临时变量,就需要编译器的处理,在c++11之前:

  1. 编译器会对返回的对象做一份临时拷贝,拷贝出一个临时对象
  2. 将原对象销毁
  3. 在将这个临时变量返回,赋值或者调用拷贝构造给main函数栈帧里的对像

会进行两次拷贝构造,编译器会对其进行优化,优化成为一次拷贝构造,但是一些老旧编译器不会优化,仍是两次拷贝构造。

如果是内置类型,那么拷贝消耗不大,但如果是自定义类型或者是stl中的容器,
如:vector<vector> vector<list>
或者你定义了一个内存消耗极大的自定义类型,如果需要拷贝的话,会有极大的消耗,于是c++11中,提出了右值引用的语义。

右值引用解决问题

可以看到,在函数返回的时候,函数的局部变量可以视为一个将亡值,也就是它的资源将要被销毁,但是如果要返回某一个局部对象,那么可不可以将该局部变量的资源进行转移,而不是对它进行拷贝后在销毁。

移动构造

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不
用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

举个例子:
这是自定义的一个string 类型的部分代码,在它每次调用拷贝或者赋值的时候会打印出调用的拷贝或者赋值类型,便于我们观察和分析。

	// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 拷贝赋值" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		 //移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;
			swap(s);
			return *this;
		}

在看一个简单的函数

string func()
		{
			string tmp("hello world!");
			return tmp;
		}


int main()
{
lx::string s1("hello  world");
lx::string s3=s1.func();

return 0;
}

可以看的,我们在func函数中返回了一个临时对象tmp,看移动拷贝是转移右值的资源的。

在这里插入图片描述
在函数中,tmp的地址为0X3353d0;
在这里插入图片描述
我们可以看的,在函数返回后,s3的地址也是0X3353d0,且tmp和s3所拥有的资源是相同的
在这里插入图片描述
说明调用了一次移动构造,直接转移了tmp中的资源,而不是在重新进行深拷贝。

我们来分析一下过程:

  1. 因为函数栈帧中,tmp对象还是一个左值,所以对其进行深拷贝,拷贝出一个临时变量。
  2. 也会对tmp进行销毁
  3. 临时变量是一个右值,所以调用一次移动构造,对s3进行构造

在这里插入图片描述
因为我用的是比较新的编译器,所以编译器对其进行了优化,所以最后显示的只有一次移动构造。

还有同学对tmp和s3的地址相同绝得很诧异。
为什么tmp都已经被销毁了,s3怎么就像是原封不动的对tmp进行copy了一样?

其实这也是编译器对此进行了一些特别复杂的处理,有兴趣的同学可以去深度挖掘一下。

STL的改动

c++11后,STL中所有容器都增加了移动构造和移动赋值。
在这里插入图片描述

c++11后,STL中所有容器插入函数中都增加了右值引用版本。
在这里插入图片描述

move()函数

这是一个帮助程序函数,用于强制对值进行移动语义, 直接使用返回值会导致被视为右值。
看以下代码:

int main()
{
	string s1("hello world");
	string s2(s1);
	string s3(move(s1));

	return 0;
}

对其进行调试,观察
对s2正常拷贝构造
在这里插入图片描述
往下走一步
在这里插入图片描述
可以看到,在对s3进行构造后,s1自身的资源被**“偷走”**了。

因为move对其进行了移动语义,转换为了右值属性,然后调用了移动构造,
将s1的资源转移了。所以会显示出这样一个结果。

所以对move()函数的调用需要谨慎。

结语

本次的博客就到这了。

我是Tom-猫,
如果觉得有帮助的话,记得
一键三连哦ヾ(≧▽≦*)o。

咱们下期再见。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营第四十二天| 背包问题

标准背包问题 有n件物品和一个最多能背重量为w 的背包。 第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 举一个例子&#xff1a; 背包最大重量为4。 物品为&#xff1a; 重量价…

5.2.3目录与文件之权限意义

现在我们知道了Linux系统内文件的三种身份&#xff08;拥有者、群组与其他人&#xff09;&#xff0c;知道每种身份都有三种权限&#xff08;rwx&#xff09;&#xff0c; 已知道能够使用chown, chgrp, chmod去修改这些权限与属性&#xff0c;当然&#xff0c;利用ls -l去观察文…

《C++高级编程》读书笔记(十:揭秘继承技术)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. 使用继承构建类 1.1 扩展类 当使用 C 编写类定义时&#xf…

WMS中Choreographer 配合 VSYNC 中断信号

WMS中Choreographer 配合 VSYNC 中断信号 1、了解SurfaceFlinger中VSYNC信号刷新2、Choreographer 舞蹈编导2.1 Choreographer初始化2.2 FrameHandler中处理任务2.3 FrameDisplayEventReceiver初始化3.4 简易流程图 3、ViewRootImpl中scheduleTraversals3.1 postCallback 通过n…

java——IO与NIO

文章目录 1. 传统IO模型字节流字符流 2. NIO模型 Java中的IO&#xff08;输入输出&#xff09;是用于在程序中读取和写入数据的一种机制。Java提供了两种不同的IO模型&#xff1a;传统的IO模型和NIO&#xff08;New IO&#xff09;模型。 1. 传统IO模型 在传统的IO模型中&…

WPF本地化/国际化,多语言切换

之前写过winformwinform使用本地化&#xff0c;中英文切换_winform 中英文切换_故里2130的博客-CSDN博客 基本的技术差不多&#xff0c;但是后来又发现了一个ResXManager工具&#xff0c;可以更好方便快捷的使用。 首先下载&#xff0c;网络不好的话&#xff0c;去官网下载&a…

01背包简介

01背包问题&#xff08;0/1 Knapsack problem&#xff09;是一个经典的动态规划问题&#xff0c;它描述了在给定容量限制的情况下&#xff0c;如何选择一组物品放入背包&#xff0c;以使得物品的总价值最大化。 问题描述&#xff1a; 假设有一个背包&#xff0c;其容量为C。现…

VulnHub项目:Fawkes

1、靶机地址 HarryPotter: Fawkes ~ VulnHub 该篇为哈利波特死亡圣器系列最终部&#xff0c;也是最难的一个靶机&#xff0c;难度真的是逐步提升&#xff01;&#xff01;&#xff01; 2、渗透过程 确认靶机IP&#xff0c;kali IP&#xff0c;探测靶机开放端口 详细的扫描…

ICLR 23 | 工业视觉小样本异常检测最新网络:Graphcore

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://openreview.net/pdf?idxzmqxHdZAwO 论文代码&#xff1a;尚未开源 1.背景 随着人工智能中深度视觉检测技术的快速发展&#xff0c;检测工业产品表面的异常/缺陷受到了前所未有…

scratch lenet(11): C语言实现 squashing function

文章目录 1. 目的2. Sigmoidal Function2.1 S2 用到 Sigmoidal Function2.2 Sigmoidal Function 的定义 3. Squashing Function3.1 改用 Sigmoid Suahsing function 术语3.2 具体到 hyperlolic tangent 这一 squahsing function 4. Squahsing function 的实现References 1. 目的…

设计模式之观察者模式笔记

设计模式之观察者模式笔记 说明Observer(观察者)目录观察者模式示例类图抽象主题角色类抽象观察者类具体主题角色类具体的观察者角色类测试类 说明 记录下学习设计模式-观察者模式的写法。JDK使用版本为1.8版本。 Observer(观察者) 意图:定义对象间的一种一对多的依赖关系&a…

Gradle构建系统macOS安装与使用

1.打开gradle.org并点击安装 2.先决条件 ,确认安装JDK1.8或者更高版本已安装 在终端输入brew install gradle进行安装 安装成功如下: 查看安装版本号gradle -v 使用gradle 1.创建目录demo并进入该目录 mkdir demo cd demo 2.gradle init 使用Gradle开始构建 输入2开始构建应…

DevOps系列文章之 docker插件实现多实例部署(IDEA插件)

1. Docker的安装以及开启远程访问 1.1 安装 # 检查虚拟机内核版本&#xff0c;必须是3.10及以上 uname -r # 安装docker yum install docker # 输入y确认安装 # 启动docker systemctl start docker # 查看docker版本 docker -v # 开机启动docker systemctl enable docker # 停…

Golang学习日志 ━━ gin-vue-admin换机重新配置的记录,很愚蠢,很傻瓜,很机械...自己使用

最近一直在弄AI&#xff0c;没时间搞gva&#xff0c;所以有点忘记了&#xff0c;代码升级管它呢&#xff0c;全部重来一遍~ 一、备份保存 根据经验和个人喜好&#xff0c;我特别不喜欢在框架下把一个应用分散在module、api、service等等目录下&#xff0c;这种目录分配方案将把…

图上作业法

目录 交通示意图的表示方法 图上作业法 &#xff08;1&#xff09;对流 &#xff08;2&#xff09;迂回 物资调运问题的图上作业法 交通路线不成圈 交通路线成圈 交通示意图的表示方法 交通示意图是用来表明收发点的大致位置、收发量、交通路线长度的图形。 图形表示…

java mail发送、接收邮件

java mail接收邮件 1、引入java mail依赖 <dependency><groupId>org.eclipse.angus</groupId><artifactId>angus-mail</artifactId><version>2.0.2</version> </dependency>2、编写代码 注意&#xff1a;下述代码中的服务器…

从BNO055传感器获取IMU数据-2

在前面的文章 从BNO055传感器获取IMU数据-1 中介绍了BNO055传感器&#xff0c;今天继续讲解应用示例。 传感器与Arduino接口 我从某宝购买了固定在带有支持组件的开发板上的 BNO055 传感器。从 Digi-Key 或贸泽购买 BNO055 并将其焊接到 7.54.4mm 28 引脚 LGA 至 DIP 转换器上…

Groovy基础

Groovy基础 学Groovy有什么用&#xff1f;&#xff1f;&#xff1f;一、Groovy简介二、Mac安装Groovy1、使用homebrew安装或官网下载2、配置环境变量3、重新加载环境变量 二、Groov基本语法三、更多特性和扩展四、Groovy简单使用1、Groovy中的字符串及三大语句结构2、Groovy类与…

1带你入门MATLAB图像处理图像类型转换(附matlab程序)

1.简述 学习目标&#xff1a; 图像类型的转换 常用图像格式 图像格式&#xff1a;是存储图像采用的文件格式。不同的操作系统、不同的图像处理软件&#xff0c;所支持的图像格式都有可能不同。 在实际应用中经常会遇到的图像格式有&#xff1a;BMP、GIF、TIFF、PCX、JPEG、P…