右值引用和移动语义以及C++11新增的类功能

正文开始前给大家推荐个网站,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

右值引用和左值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,无论左值引用还是右值引用,都是给对象取别名。
什么是左值?什么是左值引用?
左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

int main()
{// 以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;// 以下几个是对上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;delete p;return 0;
}

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

int main()
{double x = 1.1, y = 2.2;// 以下几个都是常见的右值10;x + y;fmin(x, y);// 以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);// 这里编译会报错:error C2106: “=”: 左操作数必须为左值//10 = 1;//x + y = 1;//fmin(x, y) = 1;return 0;
}

对于内置类型的右值我们称为纯右值,对于自定义类型的右值,它没有字面常量,和表达式,一般是函数的返回值,我们称为将亡值。
需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,可以理解为右值被右值引用 引用以后它的属性就变成了左值。

左值引用和右值引用的比较
左值引用

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

右值引用

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。

move可以改变变量的属性,可以将左值变为右值,但是变量本身的属性不会变,他是一个传值返回。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。

那么右值引用有什么用呢???

我们只到左值引用做参数和做返回值都可以提高效率。减少拷贝,但是当返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。如果返回的对象是一个内置类型,那消耗也不大,但是如果是一个深拷贝的对象,比如说string。

string tostring(int s)
{string ret;while (s){ret += (s % 10) + '0';s /= 10;}reverse(ret.begin(), ret.end());return ret;
}

这种情况下是无法使用左值引用返回的。因为出了作用域ret就会销毁。但是如果使用传值返回,一定会面临深拷贝的问题,消耗太大。
在这里插入图片描述
使用传值返回将会面临2次深拷贝和两次析构,消耗很大。我们想一下,ret指向的资源是出了作用域就要销毁的值,但是他指向的空间是我们需要的,我们可不可以把它指向的空间交换出来,是不是就可以减少拷贝了。

在这里插入图片描述
像ret这种的将亡值,就是我们所说的右值,所以我们可以写一个右值的拷贝构造函数,也就是移动构造,我们可以把即将销毁的值转移出来,从而达到减少拷贝的作用。移动构造中没有新开空间,拷贝数据,所以效率提高了。

string(string&& s):_str(nullptr),_size(0),_capacity(0)
{swap(s);
}

有了移动构造以后,当发生这种右值拷贝拷贝时就能减少一次深拷贝,提升还是很大的,但是拷贝完以后,这个临时对象也是一个右值,要用它去赋值给s,所以和上面同理,我们还需要一个移动赋值。

// 移动赋值
string& operator=(string&& s)
{swap(s);return *this;
}

如果我们写了移动构造又写了拷贝构造,编译器会选择最合适的调用,如果拷贝的对象是右值,那么就会调用移动构造,左值就会调用拷贝构造,如果我们不写移动构造,那么编译器就只能调用拷贝构造,const修饰的左值引用也可以引用右值。

C++11,STL的容器都是增加了移动构造和移动赋值的。

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?
有些场景下,可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。

int main()
{string s1("hello world");// 这里s1是左值,调用的是拷贝构造string s2(s1);// 这里我们把s1 move处理以后, 会被当成右值,调用移动构造// 但是这里要注意,一般是不要这样用的,因为我们会发现s1的// 资源被转移给了s3,s1被置空了。string s3(std::move(s1));return 0;
}

C++11,STL容器插入接口函数也增加了右值引用版本

万能引用和完美转发

模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,如果不加处理,接收的类型,后续使用中都退化成了左值。

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }template<typename T>
void PerfectForward(T&& t)
{//不加处理不管穿的是什么,这里的t都是左值// 如果加了move(t)那么又都会变为右值Fun(t);
}
int main()
{PerfectForward(10);  // 右值int a;PerfectForward(a);  // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);  // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

我们希望能够在传递过程中保持它的左值或者右值的属性,那么就需要用到完美转发了。

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }template<typename T>
void PerfectForward(T&& t)
{//forward<T>就是完美转发Fun(forward<T>(t));
}
int main()
{PerfectForward(10);  // 右值int a;PerfectForward(a);  // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);  // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

新的类功能

原本C++的类有6个默认成员函数,C++11 新增了两个:移动构造函数和移动赋值运算符重载

如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

类成员变量初始化
C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化。

class A
{
publicA(){}
private:int _a = 0;
};

强制生成默认函数的关键字default
C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

禁止生成默认函数的关键字delete
如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明不定义,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

那么今天的分享就到这里了,有什么不懂得可以私信博主,或者添加博主的微信,欢迎交流。

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

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

相关文章

Java多线程技术五——单例模式与多线程-备份

1 概述 本章的知识点非常重要。在单例模式与多线程技术相结合的过程中&#xff0c;我们能发现很多以前从未考虑过的问题。这些不良的程序设计如果应用在商业项目中将会带来非常大的麻烦。本章的案例也充分说明&#xff0c;线程与某些技术相结合中&#xff0c;我们要考虑的事情会…

iconify图标集离线使用方案简介

1.需求描述 前端项目&#xff0c;技术栈使用Vue3Element Plus&#xff0c;参考了ruoyi-vue-pro项目与vue-element-plus-admin项目&#xff0c;封装了一个Icon组件&#xff0c;图标使用的是iconify,项目部署在内网环境&#xff0c;不能连接互联网&#xff0c;需要部署一套iconi…

After Effects 2021 for Mac(AE 2021)

After Effects 2021是一款由Adobe公司开发的视频特效和动态图形制作软件&#xff0c;它主要用于电影、电视和网络视频的后期制作。该软件可以帮助用户创建各种令人惊叹的视觉效果&#xff0c;包括动态图形、文字特效、粒子系统、3D渲染等。 After Effects 2021提供了数百种特效…

文心一言 VS 讯飞星火 VS chatgpt (165)-- 算法导论13.1 5题

五、用go语言&#xff0c;证明&#xff1a;在一棵红黑树中&#xff0c;从某结点 x 到其后代叶结点的所有简单路径中&#xff0c;最长的一条至多是最短一条的 2 倍。 文心一言&#xff1a; 首先&#xff0c;我们要理解红黑树的性质。红黑树是一种自平衡的二叉查找树&#xff0…

天锐绿盾加密软件 | 数据保护 | 防泄密软件 \ 透明加密系统

天锐绿盾数据保护方案是一种全面的数据安全解决方案&#xff0c;旨在保护企业的电子文档和数据不被非法访问或泄露。 PC端访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是天锐绿盾数据保护方案的主要功能和特点&…

鹅目标检测数据集VOC格式350张

鹅是一种大型的水禽&#xff0c;常见于湖泊、河流和农田周围。它们的体形庞大&#xff0c;长有长颈和宽阔的翅膀&#xff0c;通常呈灰白色或棕褐色。鹅的头部呈黑色&#xff0c;嘴呈橙色&#xff0c;眼睛则是明亮的蓝色。 鹅是非常社交的动物&#xff0c;常以大群的形式生活在…

DevC++ easyx实现视口编辑,在超过屏幕大小的地图上画点,与解决刮刮乐bug效果中理解C语言指针的意义

继上篇文案&#xff0c; DevC easyx实现地图拖动&#xff0c;超过屏幕大小的巨大地图的局部显示在屏幕的方法——用悬浮窗的原理来的实现一个视口-CSDN博客 实现了大地图拖动&#xff0c;但是当时野心不止&#xff0c;就想着一气能搓啥就继续搓啥&#xff0c;看着地图移动都搓…

树莓派,mediapipe,Picamera2利用舵机云台追踪人手(PID控制)

一、项目目标 追踪人手大拇指指尖&#xff1a; 当人手移动时&#xff0c;摄像头通过控制两个伺服电机&#xff08;分别是偏航和俯仰&#xff09;把大拇指指尖放到视界的中心位置&#xff0c;本文采用了PID控制伺服电机 Mediapipe Hand简介 MediaPipe 手部标志任务可检测图像…

链接世界与中国时尚文化,积萨伯爵国际时尚品牌在中国大放异彩

时尚的历史是一部文化发展的历史。从中国古代到现代西方&#xff0c;每个时代的时尚都有其独特的文化背景和历史意义。自丝绸之路开启了古代中国与罗马帝国之间的贸易&#xff0c;时尚的不断创新和变革&#xff0c;是文化变迁和时代精神的反映。时尚的变化&#xff0c;也引领着…

【Jmeter】Jmeter基础9-BeanShell介绍

3、BeanShell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法。 3.1、Jmeter中使用的BeanShell 在Jmeter中&#xff0c;除了配置元件&#xff0c;其他类型的元件中都有BeanShell。BeanShell 是一种完全符合Java语法规范的脚本语言,并且又拥…

华为防火墙双机热备

实验需求&#xff1a; 如图所示&#xff0c;PC1为公司内部网络设备&#xff0c;AR1为出口设备&#xff0c;在FW1和FW2上配置双机热备&#xff0c;当网络正常时PC1访问AR1路径为FW1-AR1&#xff0c;当FW1出现故障后&#xff0c;切换路径为FW2-AR1。 实现目的&#xff1a; 了解…

抖店商品卡运营两个月,店铺只出了几十单,这个店还有必要做吗?

我是王路飞。 现在的抖店&#xff0c;很多商家都感觉“内卷”、“不好做”、“做不下去”、“不赚钱”...... 其实&#xff0c;当你自己做不起来的时候&#xff0c;你就只能看到跟你一样遭遇的同行不好的消息。 而那些做起来的商家&#xff0c;他们不仅不会向别人发布一些负…

RK3588平台开发系列讲解(AI 篇)RKNN rknn_query函数详细说明

文章目录 一、查询 SDK 版本二、查询输入输出 tensor 个数三、查询输入 tensor 属性(用于通用 API 接口)四、查询输出 tensor 属性(用于通用 API 接口)五、查询模型推理的逐层耗时六、查询模型推理的总耗时七、查询模型的内存占用情况八、查询模型里用户自定义字符串九、查询原…

Mysql 将数据按照年月分组 统计

要的效果: 方案&#xff1a; ① 使用 DATE_FORMAT(date, ‘%Y-%m-%d’) 函数 DATE_FORMAT 怎么去使用格式化&#xff0c;取决于后面的格式模式。 我们这里只是想区分到年 、月&#xff0c; 所以我们的sql 里面使用 %Y-%m : SELECT DATE_FORMAT(create_time, %Y-%m) AS …

【cesium-5】鼠标交互与数据查询

scene.pick返回的是包含给定窗口位置基元的对象 scene.drillpack返回的是给定窗口位置所有对象的列表 Globe.pick返回的是给光线和地形的交点 Cesium.ScreenSpaceEventType.MIDDLE_CLICK 鼠标中间点击事件 Cesium.ScreenSpaceEventType.MOUSE_MOVE 鼠标移入事件 Cesium.ScreenS…

支持多医院使用的云HIS医院信息化管理系统源码 SaaS模式

一、什么是HIS系统 HIS系统&#xff08;Hospital InformationSystem&#xff09;是医院信息化建设的核心组成部分&#xff0c;它是为了管理和运营医院而设计和开发的一套综合性的信息系统。HIS系统通过整合医院各个部门和业务流程的数据和信息&#xff0c;实现了医院内部的信息…

Uncaught ReferenceError: VueRouter is not defined

没有引入完全&#xff0c;报缺什么就引入什么 import * as VueRouter from vue-router;

ElasticSearch 文档操作

批量操作 语法 批量操作对json有严格的要求&#xff0c;每个json串不能换行&#xff0c;只能放在同一行&#xff0c;相邻的json串之间必须要有换行。每个操作必须是一对json串&#xff08;delete语法除外&#xff09; { action: { metadata }} { request body } { ac…

[幻灯片]软件需求设计方法学全程实例剖析-01-概述

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 pdf下载&#xff1a;umlchina.com/training/umlchina_01_overview.pdf UMLChina公众号精选&#xff08;20231222更新&#xff09;按ABCD工作流分类

格密码基础:垂直子空间与子格,q-ary垂直格

目录 一.写在前面 二.子空间垂直 2.1 理论解释 2.2 举例分析 三. 零空间 3.1 零空间与q-ary垂直格 3.2 零空间与行/列空间 四. 格密码相关 一.写在前面 格密码中的很多基础原语都来自于线性代数的基本概念&#xff0c;比如举几个例子&#xff1a; 格密码中的非满秩格…