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

news2025/3/16 5:03:33

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

右值引用和左值引用

传统的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.coloradmin.cn/o/1336758.html

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

相关文章

移动端防截屏录屏技术在百度账户系统实践

作者 | Seven 导读 在移动端应用的开发过程中&#xff0c;保护用户隐私和应用内敏感信息安全是一个不可忽视的课题。随着诈骗手段的升级&#xff0c;“共享屏幕”被诈骗分子频频使用&#xff0c;因为密码被泄露而导致受害者财物受损的事情层出不穷。只要开启了“共享屏幕”–本…

vue3使用Cascader联级选择器的懒加载+回显

效果图 页面代码 // separator是改变文字链接的方式&#xff0c; <el-cascaderseparator"-"v-model"currentRegionList":props"DeptRegionList":options"getRegionList"change"handleRegionListFun"ref"deptRegio…

Java——Windows系统怎么查看某个端口被占用和杀死进程解除占用,看这一篇就够了!!!

Windows系统端口占用 1. netstat -ano2. 查看某端口占用的PID3. tasklist|findstr "PID"4. 解除端口占用 总结 本篇文章介绍一下windows系统中怎么查看某个端口被占用以及如何解除占用。 1. netstat -ano 作用&#xff1a;查看系统中所有端口的占用情况 可以看到本…

面对勒索,金融机构该怎么办?

就在近期&#xff0c;某大行美国子公司被勒索软件攻击&#xff0c;使得部分交易系统中断&#xff0c;该行也在网站确认遭受了勒索软件攻击。这再次引发了金融机构对勒索软件的警惕与担忧&#xff0c;也凸显了一个重要的安全问题&#xff1a;犯罪分子不仅能窃取财产&#xff0c;…

MessageBox和HubSpot:数字化时代综合营销引擎

在数字时代&#xff0c;社交媒体已经成为企业与客户互动的重要平台。然而&#xff0c;随着信息的爆炸性增长&#xff0c;有效管理社交互动变得愈发具有挑战性。企业需要在海量信息中找到并回应关键的用户消息&#xff0c;这正是数字时代社交互动面临的主要挑战。为了解决这一问…

搭建Nginx文件下载站点

一、下载Nginx 首先&#xff0c;确保你的服务器上已经安装了Nginx&#xff0c;使用编译安装&#xff0c;下载最新版Nginx。 wget https://nginx.org/download/nginx-1.25.3.tar.gz tar -xf nginx-1.25.3.tar.gz二、安装Fancyindex和Nginx-Fancyindex-Theme模块 # 下载Fancyin…

k8s中的整体架构 ,pod含义,服务类型,网络通讯等

k8s中的整体架构 &#xff0c;pod含义&#xff0c;服务类型&#xff0c;网络通讯等 k8s整体架构pod内部和pod之间的通讯k8s的组件 k8s整体架构 上图中&#xff0c;较大的红框是k8s中的master节点&#xff0c;负责接受请求&#xff0c;调度任务&#xff0c;管理节点等&#xff0…

【网络奇缘】——奈氏准则和香农定理从理论到实践一站式服务|计算机网络

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 失真 - 信号的变化 影响信号失真的因素&#xff1a; ​编辑 失真的一种现象&#xff1a;码间…

推广主要指标及定义

推广主要指标以直通车为例解释&#xff0c;如图所示 1.展示量&#xff1a;当消费者搜索某个词&#xff0c;推广计划在天猫直通车展示位上被买家看到的次数&#xff08;去掉被消费者快进划过、主图未完金展现等情况产生的曝光)&#xff1b; 2.点击量&#xff1a;消费者看到广告…

iPad绘画之旅:从小白到文创手账设计的萌系简笔画探索

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 iPad的出现&#xff0c;不仅改变了我们对电子设…

[足式机器人]Part4 南科大高等机器人控制课 CH11 Bascis of Optimization

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;CLEAR_LAB 笔者带更新-运动学 课程主讲教师&#xff1a; Prof. Wei Zhang 课程链接 &#xff1a; https://www.wzhanglab.site/teaching/mee-5114-advanced-control-for-robotics/ 南科大高等机器人控制课 Ch11 Bascis o…

QT小技巧 - 使用QMovie进行gif切帧

简介 使用QMovie 将 gif 进行切帧&#xff0c; magick 进行合并代码 QString gifPath "E:\\workspace\\qt\\gif2imgs\\203526qre64haq3ccoobqi.gif"; // 你的图片QMovie movie(gifPath); movie.setCacheMode(QMovie::CacheNone);qDebug() << movie.frameCou…

Blender动画怎么云渲染?一分钟学会渲染速度提升数十倍

1.Blender为什么需要渲染&#xff1f; Blender是一款功能强大且备受欢迎的开源3D建模和渲染软件&#xff0c;为设计师、艺术家和动画制作人提供了无尽的创作可能。随着其视觉质量和功能的不断提高和完善&#xff0c;Blender项目的渲染时间也显著增加&#xff0c;越来越多的设计…

电子信息工程学Python C++还是Java?

电子信息工程学Python C还是Java? 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&a…

flex 属性3

order 属性定义项目的排列顺序。数值越小&#xff0c;排列越靠前&#xff0c;默认为0 。 通过align-self 属性&#xff0c;可以单独调整某个伸缩项目的对齐方式 默认值为 auto &#xff0c;表示继承父元素的 align-items 属性。

西南科技大学计算机网络实验二 (IP协议分析与以太网协议分析)

一、实验目的 通过分析由跟踪执行traceroute程序发送和接收捕获得到的IP 数据报,深入研究在IP 数据报中的各种字段,理解IP协议。基于ARP命令和Ethereal进行以太网帧捕获与分析,理解和熟悉ARP协议原理以及以太网帧格式。 二、实验环境 与因特网连接的计算机网络系统;主机操…

Bluetooth Mesh 入门学习干货,参考Nordic资料(更新中)

蓝牙网状网络&#xff08;Bluetooth mesh&#xff09;概念 概述 蓝牙Mesh Profile | Bluetooth Technology Website规范&#xff08;Mesh v1.1 后改名Mesh ProtocolMesh Protocol | Bluetooth Technology WebsiteMesh Protocol&#xff09;是由蓝牙技术联盟(Bluetooth SIG)开…

SpringBoot 3.2.0 基于SpringDoc接入OpenAPI实现接口文档

依赖版本 JDK 17 Spring Boot 3.2.0 SpringDoc 2.3.0 工程源码&#xff1a;Gitee 导入依赖 <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEnco…

【Ubuntu无法进入系统问题原因及解决方法】

有时因为某些原因&#xff0c;导致破坏了系统使用的某些库&#xff0c;比如libc.so.6被重命名&#xff0c;新替换的库无法兼容。会导致重启后&#xff0c;系统无法启动的现象。 可以通过recovery mode&#xff0c;先进入到系统中&#xff0c;更正错误内容即可。