C++11|左、右值引用和移动语义

目录

一、左值引用 && 右值引用

1.1什么是左值?什么是左值引用?

 1.2什么是右值?什么是右值引用?

 1.3左值引用与右值引用比较

 二、左、右值引用使用场景(为什么要有右值引用?)

2.1左值引用使用场景:

2.2右值引用使用场景(移动语义):

2.2.1移动构造

2.2.2移动赋值

2.2.3右值引用左值(move)


在上一篇章,我们讲了C++11提供的新接口,但是用的上的又很少,所以这些接口的意义又何在?实则,是为了这一篇章做准备,我们来继续学习

一、左值引用 && 右值引用

想必这个大家都很熟悉,在编译器上有时会看到一些左、右值这样的报错,左值引用虽然并不是c++11新增的,但是我们并没有真正去了解它,而右值引用是c++11新增的语法特性。不管是左值引用还是右值引用,都是给对象取别名。

1.1什么是左值?什么是左值引用?

左值:是一个表示数据的表达式(变量名或解引用的指针等),但是它有一定的规则:左值是可以被取地址+左值可以出现在赋值符号的左边。定义时const修饰后的左值,不能给它赋值,但是可以取它的地址。

注意:左值,并不代表它一定是在赋值符号的左边,它只是一个表示数据的表达式,通常出现在左边,要判断是不是左值,得判断是否可以取地址即可。

左值引用:对左值进行引用,即对左值取别名

通过代码来演示、验证:

#include <iostream>
using namespace std;
int main()
{//一下a、b、p、c都是左值int a = 1;int b = 2;int* p = new int(0);const int c = 3;//对上面左值的引用,可以看出左值在赋值符号的右边,要判断是否为左值,就判断是否可以取地址int*& ptr = p;int& ra = a;int& rb = b;const int& rc = c;int& pp = *p;cout << p << endl << a << endl << b << endl << c << endl;cout << ptr << endl << ra << endl << rb << endl << rc << endl << pp << endl;return 0;
}

输出结果:

 1.2什么是右值?什么是右值引用?

右值:也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值等,同样有一定的规则:右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,且右值不能被取地址。右值就相当于本身是不可被修改的值,类似于常量,既然不可修改,就不能够被取地址。

右值引用:对右值的引用,即给右值取别名。这里就要引入新的引用符号,右值引用符号:&&

注意:虽然右值本身不能被取地址,但是当右值被取别名时,也就是赋值给了引用变量,这个引用变量是可以被取地址的,不要混淆了。

通过代码演示、验证:

#include <iostream>
using namespace std;
int fun(int a, int b)
{return 3;
}
int main()
{int a = 1;int b = 2;//右值,不可修改10;a + b;//对右值的引用int&& ra = 10;int&& sum = a + b;int&& ab = fun(a, b);//右值引用变量是可以被取地址的,可以被修改。int* aa = &ra;*aa = 5;//与此,ra也被改成了5//这些都是会报错的,右值是不可别修改的,=的左操作数必须是左值。/*10 = 1;x + y = 3;fun(a,b)=3*/cout << ra << endl << sum << endl << ab << endl << *aa << endl;return 0;
}

输出结果:

 1.3左值引用与右值引用比较

左值引用总结:

1.左值引用只能引用左值,不能引用左值

2.但是const+左值引用既可引用左值,也可引用右值。

右值引用总结:

1.右值引用只能引用右值,不能引用左值

2.但是右值引用可以move以后引用左值(待后续讲解) 

 二、左、右值引用使用场景(为什么要有右值引用?)

2.1左值引用使用场景:

作参数和返回值都可以提高效率,当我们传参时,以及返回对象时,可以减少对象的拷贝从而提高了效率。例如:

#include <iostream>
using namespace std;
string fun(string& s)//使用左值引用做参数减少对象的拷贝
{s = "i love you too";return s;
}
int main()
{string str = "i love you";cout << str << endl;const string s = fun(str);cout << s << endl;return 0;
}

左值引用的缺陷:

通过一组代码来演示

#include <iostream>
#include <assert.h>
using namespace std;
namespace bit
{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){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(const string& s) -- 深拷贝" << endl;/*string tmp(s);swap(tmp);*/if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}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};bit::string to_string(int x){bit::string ret;while (x){int val = x % 10;x /= 10;ret += ('0' + val);}reverse(ret.begin(), ret.end());return ret;}
}int main()
{bit::string s = bit::to_string(1234);//拷贝构造(ret返回给to_string)+拷贝构造(to_string拷贝给s)优化为拷贝构造return 0;
}

 当函数返回对象是一个局部变量,出了函数作用域就销毁了,就不能使用左值引用返回,只能传值返回,上述代码亦如此,ret是一个局部变量,只能传值返回,传值返回会导致至少1次的拷贝构造,如图:

当然新的编译器可能会优化一次拷贝构造,但这都是编译器做的事,并没有解决本质上的问题:对于拷贝构造而言,其实现是一种深拷贝。那么对此问题,右值引用便体现出了价值,c++11新增了两个默认成员函数(移动构造、移动赋值)如下

2.2右值引用使用场景(移动语义):

2.2.1移动构造

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

对于移动构造而言,采用的是右值引用,可以直接将资源取过来(取别名),而不用进行深拷贝,所以,移动构造,就是窃取别人的资源来构造自己。如图:

注意:如果没有移动构造,调用就会匹配调用拷贝构造!

同理,赋值重载也是深拷贝,所以除了移动构造,还有移动赋值

2.2.2移动赋值

        // 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s)-- 移动赋值" << endl;swap(s);return *this;}

移动赋值也是采用右值引用,直接把资源取过来,而不用深拷贝。如图: 

注意: 如果没有移动赋值,调用就会匹配调用赋值重载!

在c++11中,对于STL容器都是增加了移动构造和移动赋值的

2.2.3右值引用左值(move)

前面提到了,右值引用可以通过move实现引用左值,那么现在来具体讲讲它的用法。std::move()函数位于头文件中,该函数作用就是将一个左值强制转换成右值引用,然后实现移动语义。

如下,main函数前面的代码还是跟之前一样(加了移动构造和移动赋值)

int main()
{bit::string s = bit::to_string(1234);//移动构造bit::string s1("hello world");//由于s1是左值引用,可以被取地址,所以这是一个拷贝构造bit::string s2(s1);//对s1进行move处理以后,s1被强制转换成右值引用,那么该表达式就会调用移动构造//但是并不建议这样用,因为移动构造会将s3,s1的资源交换,s1就会被置空了。bit::string s3 = move(s1);return 0;
}

对于STL容器的插入接口也增加了右值引用。

总结一句话是右值移动语义针对的是深拷贝问题,对于浅拷贝没有任何意义。

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

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

相关文章

秋招突击——设计模式补充——简单工厂模式和策略模式

文章目录 引言正文简单工厂模式策略模式策略模式和工厂模式的结合策略模式解析 总结 引言 一个一个来吧&#xff0c;面试腾讯的时候&#xff0c;问了我单例模式相关的东西&#xff0c;自己这方面的东西&#xff0c;还没有看过。这里需要需要补充一下。但是设计模式有很多&…

数字IC设计-VCS和Verdi的使用

#学习记录# 前言&#xff1a;本文以一个简单的计数器来说明vcs和verdi的使用 1 代码文件 1.1 计数器代码 //Engineer&#xff1a;Mr-pn-junction module counter(input clk,input rst,output reg [5:0] count); always(posedge clk or negedge rst)beginif(!rst)coun…

【算法】(C语言):二分查找

二分查找&#xff1a; 获取查找区域的中间位置。若中间位置的数据就是要找的值&#xff0c;则返回true。若要找的值 小于 中间位置的数据&#xff0c;则往左边查找。若要找的值 大于 中间位置的数据&#xff0c;则往右边查找。重复1和2&#xff0c;若没有要找的值&#xff0c;…

业界数据架构的演变

目录 一、概述 二、业务处理-单体架构 三、业务处理-微服务架构 四、数据分析-大数据Lambda架构 五、数据分析-Kappa架构 六、数据分析-LambdaKappa混合架构 七、湖仓一体架构 一、概述 近年来随着越来越多的大数据技术被开源&#xff0c;例如&#xff1a;HDFS、Spark等…

【Linux进程通信】共享内存

目录 共享内存函数 头文件 shmget ftok函数​ shmat shmdt shmctl 共享内存区是最快的IPC 形式。一旦这样的内存映射到共享它的进程的地址空间&#xff0c;这些进程间数据传递不再涉及到操作系统内核&#xff0c;换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据…

《向量数据库指南》——Milvus Cloud查询增强如何提升 RAG Pipeline 效果?

查询增强 1.假设性问题 这一方法首先使用LLM为每一条文档块生成一些假设问题,这些文档块可以回答与之对应的假设问题。在RAG阶段,进行一个query-to-query的搜索,先搜索到相关的假设问题,然后找到对应的文档块,再通过它们生成最后的回答。 这里的query-to-query指的是embed…

【数据库】仓库管理数据库(练习样例)

某连锁超市需要设计实现一个仓库管理系统&#xff0c;要求每个仓库可以有多名仓库管理员&#xff0c;每个仓库管理员只负责管理一个仓库&#xff0c;同时每个仓库都配备了一名仓库主管&#xff1b;不同的仓库存放的是不同类型的货品&#xff0c;每种货品只存放在固定的仓库中&a…

R语言数据分析案例40-华安中证基金多元线性回归和分析预测

一、引言 基金是一种非常受欢迎的投资工具&#xff0c;由于其具有风险分散、专业管理等特点&#xff0c;越来越多的投资者选择将资金投入基金市场。然而&#xff0c;基金的表现却受到很多因素的影响&#xff0c;如股票市场的整体表现、基金管理人员的能力、基金规模、基金管理…

二二复制模式,发展下属并形成一个销售网络体系来实现收入增长!

二二复制模式&#xff0c;又称为双轨制&#xff0c;是一种直销理念的营销模式&#xff0c;其核心在于通过发展下属并形成一个销售网络体系来实现收入增长。以下是对二二复制模式的详细讲解&#xff0c;包括其优势和玩法介绍&#xff0c;以及适合的行业。 一、二二复制模式的定…

Flutter集成高德导航SDK(Android篇)(JAVA语法)

先上flutter doctor&#xff1a; flutter sdk版本为&#xff1a;3.19.4 引入依赖&#xff1a; 在app的build.gradle下&#xff0c;添加如下依赖&#xff1a; implementation com.amap.api:navi-3dmap:10.0.700_3dmap10.0.700navi-3dmap里面包含了定位功能&#xff0c;地图功能…

香橙派AIpro开发板评测:部署yolov5模型实现图像和视频中物体的识别

OrangePi AIpro 作为业界首款基于昇腾深度研发的AI开发板&#xff0c;自发布以来就引起了我的极大关注。其配备的8/20TOPS澎湃算力&#xff0c;堪称目前开发板市场中的顶尖性能&#xff0c;实在令人垂涎三尺。如此强大的板子&#xff0c;当然要亲自体验一番。今天非常荣幸地拿到…

边界无限陈佩文:红蓝对抗安全演练常态化的各方分析

虽然常态化演练尚未正式开始&#xff0c;但我们仍然希望对各方的表现进行一些分析和预测&#xff0c;以辅助我们对市场的判断和决策。同时&#xff0c;也希望通过这些初步的见解&#xff0c;抛砖引玉&#xff0c;引发更多有价值的讨论和观点。 “船停在码头是最安全的&#xf…

Hi3861 OpenHarmony嵌入式应用入门--SNTP

sntp&#xff08;Simple Network Time Protocol&#xff09;是一种网络时间协议&#xff0c;它是NTP&#xff08;Network Time Protocol&#xff09;的一个简化版本。 本项目是从LwIP中抽取的SNTP代码&#xff1b; Hi3861 SDK中已经包含了一份预编译的lwip&#xff0c;但没有…

线程间的通信

文章目录 线程间的通讯技术就是通过等待和唤醒机制&#xff0c;来实现多个线程协同操作完成某一项任务&#xff0c;例如经典的生产者和消费者案例。等待唤醒机制其实就是让线程进入等待状态或者让线程从等待状态中唤醒&#xff0c;需要用到两种方法&#xff0c;如下&#xff1a…

红蓝对抗下的内网横向移动渗透技术详解

一、利用Windows计划任务横向移动 Windows计划任务是一个非常实用的功能&#xff0c;可以帮助我们自动完成一些重复性的任务。比如&#xff0c;我们可以设定一个计划任务来自动备份文件、更新软件、执行脚本等,本文主要介绍了如何利用Windows计划任务进行横向渗透。 &#xf…

C#委托事件的实现

1、事件 在C#中事件是一种特殊的委托类型&#xff0c;用于在对象之间提供一种基于观察者模式的通知机制。 1.1、事件的发送方定义了一个委托&#xff0c;委托类型的声明包含了事件的签名&#xff0c;即事件处理器方法的签名。 1.2、事件的订阅者可以通过运算符来注册事件处理器…

Python基础小知识问答系列-过滤列表元素

1. 问题&#xff1a; 如何根据单一条件过滤列表的元素&#xff1f; 如何根据复杂条件过滤列表的元素&#xff1f; 2. 解决方式&#xff1a; 可以使用推导式生成器&#xff0c;进行单一条件的列表元素过滤&#xff0c;尤其是列表内容较多时; 也可以使用filter函数进行列…

Linux Static Keys和jump label机制

文章目录 前言一、asm goto二、API使用2.1 低版本API2.2 高版本API 三、jump label四、源码分析4.1 数据结构4.2 static_key_false4.3 jump_label_init4.4 __jump_label_transform4.5 static_key_slow_inc/dec 五、__jump_table节5.1 内核5.2 内核模块 六、修改内存代码6.1 x86…

24西安电子科技大学数学与统计学院—考研录取情况

24西安电子科技大学—数学与统计学院—考研录取统计 01、数学与统计学院各个方向 02、24数学与统计学院近三年复试分数线对比 数统院24年院线相对于23年院线增加高达30分&#xff0c;确实增长浮动比较高&#xff0c;接近30分的水平&#xff0c;因此大家更需要好好去努力&#x…

GTest和Catch2单元测试学习(附Cmake测试代码库)

kevin_CTest CTest 单元测试学习 Gitee库&#xff1a; https://gitee.com/bigearrabbit/kevin_ctest.git 示例多是从网页文章上摘取的&#xff0c;大部分记录在下面&#xff0c;或者源码内。供学习参考。 CTest 学习Catch2 框架 单个文档的测试架构&#xff0c;使用方便&am…