左值引用、右值引用及移动语义

个人主页:Lei宝啊 

愿所有美好如期而遇


左值

概念

可以取到地址的值就是左值,并且一般情况下可以修改(const类型左值不可修改)。

左值举例:

//左值
int a = 0;
const int b = 1;
int* p = &a;

右值

概念

不能取到地址的值就是右值,并且右值不能被修改。(字面常量,表达式返回值,函数返回值(非左值引用)),我们之前使用的就是左值引用。

右值举例:

int func()
{return 1 + 1;
}//右值
10;
a + b;
func();

左值引用

概念

左值引用就是给左值的引用,给左值取别名。
int a = 0;const int b = 1;int* p = &a;//左值引用int& c = a;const int& d = b;int*& e = p;

右值引用

概念

右值引用就是给右值的引用,给右值取别名。

	10;a + b;func();//右值引用int&& e = 10;int&& f = a + b;int&& g = func();

互相引用 

左值引用右值需要加const,右值引用左值需要将左值先进行move。

	//左值引用右值const int& f = 10;//右值引用左值int&& g = move(a);

应用与解释

简单来说就是为什么需要右值引用,我们先来看例子:

也就是说,临时对象的产生其实是多余的,所以在没有右值引用时,编译器给出的优化方案就是优化掉临时对象,直接使func中vv拷贝构造main中vv,可即便如此,还是有一次无谓的拷贝,就是func中的vv,他仍要销毁,资源还是浪费了。

接下来使用我们自己实现的string,来对有无右值引用做对比。

#include <iostream>
#include <string>
#include <string.h>
#include <cassert>
#include <algorithm>using namespace std;namespace own
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造 -- 左值string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;_str = new char[s._capacity+1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}// 拷贝赋值// s2 = tmpstring& operator=(const string& s){cout << "string& operator=(const string& s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\0};own::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}own::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}

这里博主使用g++编译器,并且关闭了部分优化,在Visual  Studio 2022中,优化非常大,我们看不出他的过程,无法更好地进行对比,所以这里在Linux下进行演示,并使用-fno-elide-constructors关闭g++的编译优化。

举例一:仅有左值引用

一:
int main()
{own::string ret = own::to_string(1234);return 0;
}

这也就是我们上面得出的结果。

二:
int main()
{own::string ret;ret = own::to_string(-1234);return 0;
}

这种编译器是不会优化的,所以一般来说将他们写在一行上。

举例二:加入右值引用

		// 移动构造 -- 右值(将亡值)string(string&& s){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}

我们这里为什么要加入右值引用呢?首先,右值也叫做将亡值,也就是即将销毁的值。我们希望能够将这个将亡值利用起来,拿走他的资源。

我们可以想象一下,可以拿走左值的资源吗?左值引用左值,左值可能仍要被使用,如果这么被swap拿走资源是不可以的,但是将亡值,也就是对右值这样做却是我们希望看到的。 

一:
int main()
{own::string ret = own::to_string(1234);return 0;
}

这和我们上面的结果是一致的。

二:
int main()
{own::string ret;ret = own::to_string(-1234);return 0;
}

加入移动赋值拷贝

		// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动拷贝" << endl;swap(s);return *this;}
三:
int main()
{own::string ret;ret = own::to_string(-1234);return 0;
}

看了这么久也许你有一个疑惑,移动构造和移动赋值,都要交换右值的资源,但是右值不是不能被修改吗?

那么这里我们说:右值引用 引用右值后的属性为左值。

也就是说,一个右值,被右值引用后,那个右值引用的属性将变成左值,于是swap中的s属性是左值,也就可以传过去了。

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

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

相关文章

【C语言】Infiniband驱动mlx4_init_one

一、注释 这是Linux内核中Mellanox Ethernet网卡驱动程序mlx4模块的一部分代码&#xff0c;主要用于初始化一个PCI设备。以下是其注释&#xff1a; // 驱动的主结构体&#xff0c;包含了供PCI核心使用的勾子&#xff08;hooks&#xff09; static struct pci_driver mlx4_dri…

分布式系统的发展史

目录 &#x1f433;今日良言&#xff1a;且视他人之疑目如盏盏鬼火&#xff0c;大胆地去走自己的夜路 &#x1f407;一、常见概念 &#x1f407;二、发展史 今日良言&#xff1a;且视他人之疑目如盏盏鬼火&#xff0c;大胆地去走自己的夜路 一、常见概念 在正式介绍分布式系…

【AutoML】一个用于图像、文本、时间序列和表格数据的AutoML

一个用于图像、文本、时间序列和表格数据的AutoML AutoGluon介绍安装AutoGluon快速上手 参考资料 AutoGluon自动化机器学习任务&#xff0c;使您能够在应用程序中轻松实现强大的预测性能。只需几行代码就可以训练和部署有关图像&#xff0c;文本&#xff0c;时间序列和表格数据…

Python学习:函数

函数定义 在Python中&#xff0c;函数&#xff08;Function&#xff09;是一组用于完成特定任务或计算的语句块。定义函数可以让我们将一段代码重用多次&#xff0c;提高代码的可读性和可维护性。以下是定义函数的基本语法和结构&#xff1a; def function_name(parameters):&…

开通抖音小店后要做什么?这个流程你必须知道!建议收藏避免遗漏

哈喽~我是电商月月 在入驻抖音小店前&#xff0c;大家了解的抖店步骤应该是&#xff1a;入驻-选品-找达人-售后 但真的入驻后大家可不敢这样做&#xff01;操作不当可能违规&#xff0c;严重的还会扣除保证金&#xff0c;做清店处理 这些细节流程大家一定要知道&#xff0c;…

3D开发工具HOOPS更新:高效、轻量化模型处理再突破!

随着数字化转型的深入发展&#xff0c;高性能图形显示成为了软件开发领域的重要研究方向。在众多工具和库中&#xff0c;HOOPS因其强大的三维图形处理能力而受到广泛关注。 HOOPS也与时俱进&#xff0c;持续更进与创新&#xff0c;近期又推出了一系列新功能&#xff0c;这些功…

鸿蒙开发之了解ArkTS

鸿蒙开发者官网 &#xff1a; https://developer.huawei.com/consumer/cn/ 开发鸿蒙要用的软件是 DevEco Studio ArkTS建立在JS和TS的基础之上&#xff0c;扩展了声明式UI开发范式和状态管理&#xff0c;提供更简洁和自然的开发方式。 ArkTS引入了渲染引擎的增强&#xff0c…

【Java】IDEA集成开发工具中英文切换

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章。 这是《Java》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识…

怎么批量修改文件名中的一部分?

怎么批量修改文件名中的一部分&#xff1f;批量修改文件名中的一部分文字是我们在处理大量文件时经常需要做的任务之一。这项工作可以极大地提高工作效率&#xff0c;节省宝贵的时间。无论是对于个人用户还是企业组织来说&#xff0c;都是非常实用的技能。首先&#xff0c;批量…

王者荣耀国服诸葛亮 - 教学视频(最新)

01. 英雄定位与技能机制02. 不同铭文下的不同出装&#xff08;上&#xff09;03. 不同铭文下的不同出装&#xff08;下&#xff09;04. 两道锦囊教你玩转金身05. 诸葛最无敌的2套连招06. 对线干将07. 对线扁鹊08. 对线王昭君09. 单排9胜率的黄金中单法则总概10. 中单打钱思路11…

C# 登录界面代码

背景 MVVM 是一种软件架构模式&#xff0c;用于创建用户界面。它将用户界面&#xff08;View&#xff09;、业务逻辑&#xff08;ViewModel&#xff09;和数据模型&#xff08;Model&#xff09;分离开来&#xff0c;以提高代码的可维护性和可测试性。 MainWindow 类是 View&a…

基于springboot实现网页时装购物系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现时装购物系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;时装购物系统当然也不能排除在外。时装购物系统是以实际运用为开发背景&#xff0c…