c++学习笔记(13)-左值和右值

一、左值与右值

  • 啥是左值和右值呢?

左值:在内存有确定存储地址、有变量名,表达式结束依然存在的值,简单来说左值就是非临时对象。
右值:就是在内存没有确定存储地址、没有变量名,表达式结束就会销毁的值,简单来说右值就是临时对象。

int a = 0;  // 在这条语句中,a 是左值,0 是临时值,就是右值。

左值可以分为两类:常量左值 和 非常量左值

int a=10;              	// a 为非常量左值(有确定存储地址,也有变量名)
const int a1=10;      //a1 为常量左值(有确定存储地址,也有变量名)
const int a2=20;      //a2 为常量左值(有确定存储地址,也有变量名)

同理,右值也可以分为两类:常量右值 和 非常量右值

int a=10;              	// 10 为非常量右值
const int a1=10;      
const int a2=20;      
a1+a2               	// (a1+a2) 为常量右值

二、左值引用于右值引用

  • 知道了左值与右值了,那啥是左值引用与右值引用呢?

  • 左值引用:其实就是绑定到左值的引用,通过&来获得左值引用。

    • 左值引用举例
int a=10;              //非常量左值(有确定存储地址,也有变量名)
const int a1=10;       //常量左值(有确定存储地址,也有变量名)
const int a2=20;       //常量左值(有确定存储地址,也有变量名)
 
//非常量左值引用
int &b1=a;            //正确,a是一个非常量左值,可以被非常量左值引用绑定
int &b2=a1;           //错误,a1是一个常量左值,不可以被非常量左值引用绑定
int &b3=10;           //错误,10是一个非常量右值,不可以被非常量左值引用绑定
int &b4=a1+a2;        //错误,(a1+a2)是一个常量右值,不可以被非常量左值引用绑定

//常量左值引用
const int &c1=a;      //正确,a是一个非常量左值,可以被非常量右值引用绑定
const int &c2=a1;     //正确,a1是一个常量左值,可以被非常量右值引用绑定
const int &c3=a+a1;   //正确,(a+a1)是一个非常量右值,可以被常量右值引用绑定
const int &c4=a1+a2;  //正确,(a1+a2)是一个常量右值,可以被非常量右值引用绑定

总结归纳:非常量左值引用只能绑定到非常量左值上;常量左值引用可以绑定到非常量左值、常量左值、非常量右值、常量右值等所有的值类型

  • 右值引用:其实也是绑定到右值的引用,通过&&来获得右值引用。

    • 左值引用举例:
int a=10;             //非常量左值(有确定存储地址,也有变量名)
const int a1=20;      //常量左值(有确定存储地址,也有变量名)
const int a2=20;      //常量左值(有确定存储地址,也有变量名)

//非常量右值引用
int &&b1=a;            //错误,a是一个非常量左值,不可以被非常量右值引用绑定
int &&b2=a1;           //错误,a1是一个常量左值,不可以被非常量右值引用绑定
int &&b3=10;           //正确,10是一个非常量右值,可以被非常量右值引用绑定
int &&b4=a1+a2;        //错误,(a1+a2)是一个常量右值,不可以被非常量右值引用绑定

//常量右值引用
const int &&c1=a;      //错误,a是一个非常量左值,不可以被常量右值引用绑定
const int &&c2=a1;     //错误,a1是一个常量左值,不可以被常量右值引用绑定
const int &&c3=a+a1;   //正确,(a+a1)是一个非常量右值,可以被常量右值引用绑定
const int &&c4=a1+a2;  //正确,(a1+a2)是一个常量右值,不可以被常量右值引用绑定

总结归纳:非常量右值引用只能绑定到非常量右值上;常量右值引用可以绑定到非常量右值、常量右值上。
image.png
从上述可以发现,常量左值引用可以绑定到右值上,但右值引用不能绑定任何类型的左值,若想利用右值引用绑定左值该怎么办呢?

  • C++11中提供了一个标准库move函数获得绑定到左值上的右值引用,即直接调用std::move告诉编译器将左值像对待同类型右值一样处理,但是被调用后的左值将不能再被使用。
  • std::move()函数举例
int a=10;                 //非常量左值(有确定存储地址,也有变量名)
const int a1=20;          //常量左值(有确定存储地址,也有变量名)

//非常量右值引用
int &&d1=std::move(a);    //正确,将非常量左值a转换为非常量右值,可以被非常量右值引用绑定
int &&d2=std::move(a1);    //错误,将常量左值a1转换为常量右值,不可以被非常量右值引用绑定

//常量右值引用
const int &&c1=std::move(a);      //正确,将非常量左值a转换为非常量右值,可以被常量右值引用绑定
const int &&c2=std::move(a1);     //正确,将常量左值a1转换为常量右值,可以被常量右值引用绑定

最后可以发现,编译器利用std::move将左值强制转换为相同类型的右值之后,引用情况跟右值是一模一样的。

三、右值引用与左值引用的区别

  • 1、左值引用绑定到有确定存储空间以及变量名的对象上,表达式结束后对象依然存在;
  • 2、右值引用绑定到要求转换的表达式、字面常量、返回右值的表达式等临时对象上,赋值表达式结束后就对象就会被销毁。
  • 3、左值引用后可以利用别名修改左值对象;右值引用绑定的值不能修改

四、引入右值引用的原因

  • 1、替代需要销毁对象的拷贝,提高效率:某些情况下,需要拷贝一个对象然后将其销毁,如:临时类对象的拷贝就要先将旧内存的资源拷贝到新内存,然后释放旧内存,引入右值引用后,就可以让新对象直接使用旧内存并且销毁原对象,这样就减少了内存和运算资源的使用,从而提高了运行效率;
  • 2、移动含有不能共享资源的类对象:像IO、unique_ptr这样的类包含不能被共享的资源(如:IO缓冲、指针),因此,这些类对象不能拷贝但可以移动。这种情况,需要先调用std::move将左值强制转换为右值,再进行右值引用。
class MyString {
private:
	char* _data;
	size_t   _len;
	void _init_data(const char *s) {
		_data = new char[_len + 1];
		memcpy(_data, s, _len);
		_data[_len] = '\0';
	}
public:
	MyString() {
		_data = NULL;
		_len = 0;
	}

	MyString(const char* p) {
		_len = strlen(p);
		_init_data(p);
	}

	MyString(const MyString& str) {
		_len = str._len;
		_init_data(str._data);
		std::cout << "Copy Constructor is called! source: " << str._data << std::endl;
	}

	MyString& operator=(const MyString& str) {
		if (this != &str) {
			_len = str._len;
			_init_data(str._data);
		}
		std::cout << "Copy Assignment is called! source: " << str._data << std::endl;
		return *this;
	}

	virtual ~MyString() {
		if (_data != NULL) {
			std::cout << "Destructor is called! " << std::endl; 
			free(_data);
		}
	}
};

int main() { 
	MyString a; 
	a = MyString("Hello"); 
	std::vector<MyString> vec; 
	vec.push_back(MyString("World")); 
}

运行结果:

Copy Assignment is called! source: Hello
Destructor is called!
Copy Constructor is called! source: World
Destructor is called!
Destructor is called!
Destructor is called!

总共执行了2次拷贝,MyString(“Hello”)和MyString(“World”)都是临时对象,临时对象被使用完之后会被立即析构,在析构函数中free掉申请的内存资源。 如果能够直接使用临时对象已经申请的资源,并在其析构函数中取消对资源的释放,这样既能节省资源,有能节省资源申请和释放的时间。 这正是定义转移语义的目的。
通过加入定义转移构造函数和转移赋值操作符重载来实现右值引用(即复用临时对象):

MyString(MyString&& str) { 
		std::cout << "Move Constructor is called! source: " << str._data << std::endl; 
		_len = str._len; 
		_data = str._data; 
		str._len = 0; 
		str._data = NULL;   // ! 防止在析构函数中将内存释放掉
	}

	MyString& operator=(MyString&& str) { 
		std::cout << "Move Assignment is called! source: " << str._data << std::endl; 
		if (this != &str) { 
			_len = str._len; 
			_data = str._data; 
			str._len = 0; 
			str._data = NULL;  // ! 防止在析构函数中将内存释放掉
		} 
		return *this; 
	}

运行结果:

Move Assignment is called! source: Hello
Move Constructor is called! source: World
Destructor is called!
Destructor is called!

需要注意的是:右值引用并不能阻止编译器在临时对象使用完之后将其释放掉的事实,所以转移构造函数和转移赋值操作符重载函数 中都将_data赋值为了NULL,而且析构函数中保证了_data != NULL才会释放。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Vue】高级系列(二)消息订阅与发布--$nextTick--动画效果

2.1 消息订阅与发布&#xff08;pubsub&#xff09; 一种组件间通信的方式&#xff0c;适用于任意组件间通信。 使用步骤&#xff1a; 安装pubsub&#xff1a;npm i pubsub-js 引入: import pubsub from pubsub-js 接收数据&#xff1a;A组件想接收数据&#xff0c;则在A组件…

使用克魔助手进行iOS数据抓包和HTTP抓包的方法详解

摘要 本文博客将介绍如何在iOS环境下使用克魔助手进行数据抓包和HTTP抓包。通过抓包&#xff0c;开发者可以分析移动应用程序的网络请求发送和接收过程&#xff0c;识别潜在的性能和安全问题&#xff0c;提高应用的质量和安全性。 引言 在移动应用程序的开发和测试过程中&am…

Codeforces Pinely Round 3 (Div. 1 + Div. 2) A~F

A.Distinct Buttons(思维) 题意&#xff1a; 你在开始时站在点 ( 0 , 0 ) (0,0) (0,0)&#xff0c;同时&#xff0c;手上有一个遥控器&#xff0c;上面有四个按钮&#xff1a; U:移动到 ( x , y 1 ) (x, y 1) (x,y1)的位置 R:移动到 ( x 1 , y ) (x 1, y) (x1,y)的位置 …

【《设计模式之美》】如何取舍继承与组合

文章目录 什么情况下不推荐使用继承&#xff1f;组合相比继承有哪些优势&#xff1f;使用组合、继承的时机 本文主要想了解&#xff1a; 为什么组合优于继承&#xff0c;多用组合少用继承。如何使用组合来替代继承哪些情况适用继承、组合。有哪些设计模式使用到了继承、组合。 …

数据分析-23--糖尿病预测(线性回归模型)(包含数据代码)

文章目录 0. 数据代码下载1. 项目介绍2. 数据处理1. 导入数据2. 处理数据 3. 建立模型4. 考察单个特征 0. 数据代码下载 关注公众号&#xff1a;『AI学习星球』 回复&#xff1a;糖尿病预测 即可获取数据下载。 算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号或➕v&am…

软件测试/测试开发丨Python学习笔记之基本数据类型与操作

一、变量 1、变量的定义&#xff1a; a. 在python中&#xff0c;变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间&#xff1b; b.变量的值可以被读取和修改。 2、命名规则&#xff1a; a.变量名由字母&#xff08;广义的Unic…

Appium+python自动化(一)- 环境搭建—上(超详解)

简介 今天是高考各地由于降水&#xff0c;特别糟糕&#xff0c;各位考生高考加油&#xff0c;全国人民端午节快乐。最近整理了一下自动化的东西&#xff0c;先前整理的python接口自动化已经接近尾声。即将要开启新的征程和篇章&#xff08;Appium&python&#xff09;。那么…

Python五子棋程序实现详解

Python五子棋程序实现详解 引言功能实现显示棋盘点击落子判断胜负游戏结束判断交替落子 运行结果完整代码总结 引言 五子棋是一种广泛传播的策略棋类游戏&#xff0c;两人对弈&#xff0c;通过在棋盘上落子&#xff0c;以先形成连续的相同颜色的五子棋为胜利条件。本文将介绍如…

JavaScript基础知识点总结:从零开始学习JavaScript(四)

学习目标&#xff1a; 掌握函数的基本使用&#xff0c;让代码具备复用能力 理解封装的意义&#xff0c;能够具备封装函数的能力 学习内容&#xff1a; 函数 综合案例 学习时间&#xff1a; 周一至周五晚上 7 点—晚上9点周六上午 9 点-上午 11 点周日下午 3 点-下午 6 点 学…

【零成本实现接口自动化测试】Java+TestNG 测试Restful service

接口自动化测试 – JavaTestNG 测试 Restful Web Service 关键词&#xff1a;基于Rest的Web服务&#xff0c;接口自动化测试&#xff0c;数据驱动测试&#xff0c;测试Restful Web Service&#xff0c; 数据分离&#xff0c;JavaMavenTestNG 本文主要介绍如何用Java针对Restf…

金蝶云星空反写规则表结构同步另一个数据库

文章目录 金蝶云星空反写规则表结构同步另一个数据库在BOS找到《反写规则》的表反写规则相关表创建反写规则&#xff0c;或者已经创建好的反写规则定位反写规则数据导出表数据执行脚本BOS导入数据库直接执行 金蝶云星空反写规则表结构同步另一个数据库 在BOS找到《反写规则》的…

k8s 网络

还没有部署网络。 k8s的网络类型&#xff1a; k8s中的通信模式&#xff1a; 1&#xff0c;pod内部之间容器和容器之间的通信。 在同一个pod中的容器共享资源和网络&#xff0c;使用同一个网络命名空间。可以直接通信的。 2&#xff0c;同一个node节点之内不同pod之间的通信。…

香橙派5plus从ssd启动Ubuntu

官方接口图 我实际会用到的就几个接口&#xff0c;背面的话就一个M.2固态的位置&#xff1a; 其中WIFI模块的接口应该也可以插2230的固态&#xff0c;不过是pcie2.0的速度&#xff0c;背面的接口则是pcie3.0*4的速度&#xff0c;差距还是挺大的。 开始安装系统 准备工作 一张…

开源轻量级分布式文件系统FastDFS本地部署并实现远程访问服务器

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…

算法学习系列(十三):Trie树

目录 引言一、Trie概念二、Trie树模板三、例题 引言 这个Trie还是比较有用的&#xff0c;主要的功能就是高效的存储和查找字符串的数据结构。 一、Trie概念 假设这个Trie只存储小写字母的话&#xff1a; 这个大概就是这么个概念&#xff0c;就是头结点是0号&#xff0c;然后…

使用腾讯云轻量应用服务器基于SRS搭建个人直播间

使用腾讯云轻量应用服务器基于SRS音视频服务器应用模板镜像即可一键搭建个人直播间&#xff0c;SRS Stack让你一键拥有自己的视频云解决方案&#xff0c;可以在云上或私有化部署&#xff0c;支持丰富的音视频协议&#xff0c;提供鉴权、私人直播间、多平台转播、录制、虚拟直播…

js中变量的使用

文章目录 一、变量二、声明三、赋值四、更新变量五、声明多个变量(不推荐)六、变量的本质七、关键字八、变量名命名规则 一、变量 理解变量是计算机存储数据的“容器”&#xff0c;掌握变量的声明方式 白话&#xff1a;变量就是一个装东西的盒子。通俗&#xff1a;变量是计算机…

【MySQL学习笔记007】约束

1、概述 &#xff08;1&#xff09;概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 &#xff08;2&#xff09;目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 &#xff08;3&#xff09;分类 约束 描述 关键字 …

ZStack Cube超融合一体机助力电子支付企业升级改造

电子支付服务企业实壹信息通过ZStack Cube超融合一体机为业务生产环境构建新一代云基础设施&#xff0c;结合V2V迁移模块实现ZStack社区版云平台应用迁移到全新的云基础设施ZStack Cube 超融合一体机上&#xff0c;同时共享分布式存储和外接FC-SAN存储。此外&#xff0c;运维人…

Android 8.1 设置USB传输文件模式(MTP)

项目需求&#xff0c;需要在电脑端adb发送通知手机端接收指令&#xff0c;将USB的仅充电模式更改成传输文件&#xff08;MTP&#xff09;模式&#xff0c;便捷用户在我的电脑里操作内存文件&#xff0c;下面是我们的常见的修改方式 1、android12以下、android21以上是这种方式…