【C++干货铺】C++11新特性——右值引用、移动构造、完美转发

=========================================================================

个人主页点击直达:小白不是程序媛

C++系列专栏:C++干货铺

代码仓库:Gitee

=========================================================================

目录

左值与左值引用

右值与右值引用

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

 左值引用总结:

右值引用总结:

左值引用的作用和意义

右值引用的使用场景和意义

移动赋值和移动构造

 完美转发

std::forward 完美转发在传参的过程中保留对象原生类型属性

​编辑新的类功能

默认成员函数

强制生成默认函数的关键字default

禁止生成默认函数的关键字delete

继承和多态中的final与override关键字


左值与左值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们
之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。

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

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名

//左值与左值引用
int main()
{
	//左值
	//可以取到地址
	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;
	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);

	return 0;
}

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

 左值引用总结:

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

右值引用总结:

  • 1. 右值引用只能右值,不能引用左值。
  • 2. 但是右值引用可以move以后的左值。
//左值引用和右值引用的比较
int main()
{
	//左值引用
	int a = 10;
	int& ra = a;
	//const 左值引用既可以引用左值又可以引用右值
	const int& ra1 = a;
	const int& ra2 = 10;

	//右值引用
	//不可以引用左值
	/*int&& r1 = a;*/
	//使用mova后可以
	int&& r1 = move(a);
	int&& r1 = 10;

	return 0;

}

左值引用的作用和意义

左值引用可以做函数的参数和函数的返回值,这样可以避免在函数传参和函数返回的时候去进行各种拷贝构造,对于一些大对象和需要进行深拷贝的对象来说,这样做可以提高效率。

左值引用的缺陷:当函数返回的对象是一个局部变量时,出了函数的作用域该对象就被销毁了,就不能使用左值引用返回,只能通过传值返回。而传值返回会导致至少调用一次拷贝构造,如果是旧一点的编译器可能是调用两次构造函数。。

    //MyString
    string& to_string(int x)
	{
		string ret;
		while (x)
		{
			int val = x % 10;
			x = x / 10;
			ret += ('0' + val);
		}
		reverse(ret.begin(), ret.end());
		return ret;
	}



int main()
{
	bit::string s; 
	s= to_string(1234);
	return 0;
}


右值引用的使用场景和意义

其实右值含有两种形式

  • 纯右值:编写程序时所出现的的字面值常属于纯右值
  • 将亡值:自定义类型的临时对象

右值引用的出现完全是为了解决左值引用的缺陷的;


移动赋值和移动构造

还是对上面的代码进行分析

注:上面的代码我们先生成一个临时对象ret,函数调用结束会销毁栈帧;因此ret拷贝构造在生成一个临时对象,在由这个临时对象深拷贝生成String类的s。为了实例化这个对象我们进行了两次临时对象的创建,在由这两个临时对象进行两次深拷贝;对于很大的类时,进行这样的操作是非常浪费时间和空间的;由于中间产生了临时值(这个临时值就是我们上面所说的将亡值),而恰好临时值取不到地址是一个右值,我们可以对其引用使用移动赋值。

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

只需要重载一个右值引用参数类型的重载函数即可。

函数调用结束需要销毁栈帧编译器识别ret为一个临时值,并且这个临时值需要拷贝在产生一个临时值return返回调用移动拷贝交换数据即可,释放ret;函数返回的临时值需要赋值给另外一个同类型的值,编译器识别进行移动赋值,交换数据释放临时值;这个过程就是移动赋值,及减少了临时对象的产生(将亡值),又减少了深拷贝。对于一些大的类来说进行连续的赋值节省了空间和时间。

移动构造和移动赋值的原理差不多,这里不进行过度赘述。

注:目前比较新的编译器会对连续的拷贝构造进行优化。


完美转发

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)
{
	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;
}

这是什么情况? 

模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发。

注:你也可以这样理解上面的PerfectForward函数其实是左值引用和右值引用是重载的,当传入左值时候重载,直接调用Fun函数;当传入右值时,刚好符合参数进行右值引用,但是右值的右值引用的属性是左值,因此调用Fun函数也是左值引用。

std::forward 完美转发在传参的过程中保留对象原生类型属性

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)
{
	//完美转发
	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个默认成员函数:

  • 1. 构造函数
  • 2. 析构函数
  • 3. 拷贝构造函数
  • 4. 拷贝赋值重载
  • 5. 取地址重载
  • 6. const 取地址重载

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数移动赋值运算符重载

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

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

强制生成默认函数的关键字default

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

禁止生成默认函数的关键字delete

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

继承和多态中的final与override关键字

这个我们在继承和多态章节已经进行了详细讲解这里就不再细讲,需要的话去复习继承和多态那篇文章吧。


今天给大家分享介绍了C++11中的右值引用和移动构造。如果觉得文章还不错的话,可以三连支持一下,个人主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的三连支持就是我前进的动力!

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

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

相关文章

C# Socket通信从入门到精通(17)——单个异步UDP服务器监听一个客户端C#代码实现

前言: 我们在开发UDP通信程序时,除了开发UDP同步客户端程序,有时候我们也需要开发异步UDP服务器程序,所谓的异步最常见的应用就是服务器接收客户端数据以后,程序不会卡在数据接收这里,而是可以继续往下执行,这在实际项目中是经常会遇到的,所以说掌握异步UDP服务器程序…

蓝桥杯省赛无忧 编程9

#include<bits/stdc.h> using namespace std; int main() {int n,k,ans0;cin>>n>>k;while(n--){int a;cin>>a;ansa&1;}if(ans&1) cout<<"Alice"<<\n;else cout<<"Bob"; return 0; }这个游戏是基于数…

Windows主机Navicat远程连接到Ubuntu18.04虚拟机MySQL

1. 在虚拟机上安装MySQL sudo apt-get install mysql-server sudo apt-get install libmysqlclient-dev 2. 检查安装 sudo netstat -tap | grep mysql 3. 查看默认密码 sudo cat /etc/mysql/debian.cnf 4. 用查看到的密码登录MySQL server&#xff0c;修改root用户的密码 …

OpenHarmonyOS-gn与Ninja

GN语法及在鸿蒙的使用 [gnninja学习 0x01]gn和ninja是什么 ohos_sdk/doc/subsys-build-gn-coding-style-and-best-practice.md GN 语言与操作 一、gn简介 gn是generate ninja的缩写&#xff0c;它是一个元编译系统&#xff08;meta-build system&#xff09;,是ninja的前端&am…

系统架构设计师教程(十三)层次式架构设计理论与实践

层次式架构设计理论与实践 13.1 层次式体系结构概述13.2 表现层框架设计13.2.1 表现层设计模式13.2.2 使用XML设计表现层&#xff0c;统一Web Form与Windows Form的外观13.2.3表现层中UIP设计思想13.2.4 表现层动态生成设计思想 13.3 中间层架构设计13.3.1 业务逻辑层组件设计1…

C++ | 五、哈希表 Hash Table(数组、集合、映射)、迭代器

哈希表基础 哈希表是一类数据结构&#xff08;哈希表包含数组、集合和映射&#xff0c;和前两篇文章叙述的字符串、链表平级&#xff09;哈希表概念&#xff1a;类似于Python里的字典类型&#xff0c;哈希表把关键码key值通过哈希函数来和哈希表上的索引对应起来&#xff0c;之…

对testfire.net进行信息收集,采用googlehacking语法,fofa等包括子端口号、子域名,备案信息,所属资产等等

采用被动的信息收集对testfire.net进行信息收集。 使用命令查询真实IP地址: nslookup testfire.net 使用googlehacking语法: 使用子域名查询网站查询一下子域名&#xff1a; 利用fofa查询一些信息&#xff1a; 利用whois 查找备案信息等&#xff1a; 尝试绕过千锋官网的cdn 利…

国考省考行测:选词填空,逻辑填空,语境分析,语意辨析,刷题,

国考省考行测&#xff1a;选词填空&#xff0c;逻辑填空&#xff0c;语境分析 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0…

【博士每天一篇论文-技术综述】Machine Learning With Echo State Networks 一篇系统讲解ESN知识的五星文章

阅读时间&#xff1a;2023-11-21 1 介绍 年份&#xff1a;2020 作者&#xff1a;徐元超&#xff0c;曼尼托巴大学 期刊&#xff1a; 无 引用量&#xff1a;无 这篇文章是一篇技术报告&#xff0c;从递归神经网络&#xff08;RNNs&#xff09;引入到回声状态网络&#xff08;…

基于DRIVE数据集的视网膜UNet分割

1 数据集介绍 这是一个非常小的数据集&#xff0c;非常适合用于视觉分割任务练手。数据集的文件夹如图所示&#xff1a; 图1-1文件夹结构 test中存放的是测试图片&#xff0c;training中存放的是20张用于训练的图片。imges文件夹中存放的是20张原始图片&#xff0c;mask中存放…

Leetcode的AC指南 —— 栈与队列:232.用栈实现队列

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列&#xff1a;232.用栈实现队列 **。题目介绍&#xff1a;请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a;…

解决 pnpm : 无法加载文件 C:\Program Files\nodejs\pnpm.ps1,因为在此系统上禁止运行脚本。

执行下面命令进行安装pnpm安装后 npm install -g pnpm 然后执行pnpm 报错 解决办法&#xff1a; 以管理员身份运行 Windows PowerShell &#xff0c; 在命令行输入以下命令后按回车&#xff0c; set-ExecutionPolicy RemoteSigned 再输入Y 回车即可。 再回到控制台输入p…

工作小记 cv-cuda使用

最近要实现RGB相关cuda算子的功能&#xff0c;最终通过自己手写核函数实现。这里记录一下对cv-cuda的调研和使用&#xff0c;因为项目要求gcc-5&#xff0c;而cv-cuda要求gcc11而放弃使用&#xff0c;但是相关的记录&#xff0c;以及使用方法都要记录下来&#xff0c;以便下次项…

在MD编辑器里插入20次方问题

前言 看了很多文章里面没写怎么插入20次方&#xff0c;最后在官网的一篇文章上看到了很详细的数学公式的插入。 问题 大家肯定以为这样就可以了 效果 明显是不行的 解决 使用{}把数字括起来就可以了。 1 20 1^{20} 120 小知识 在行内显示(就是与文字在一起) $ $另起…

《A++ 敏捷开发》- 5 量化管理从个人开始

我&#xff1a;你们管理层和客户都比较关心项目的进度&#xff0c;项目是否能按时完成&#xff1f;请问你们过去的项目如何&#xff1f; 开发&#xff1a;我们现在就是走敏捷开发&#xff0c;两周一个迭代。每次迭代前&#xff0c;我们聚一起开会&#xff0c;把所有用户故事按优…

互联网加竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

(超详细)9-YOLOV5改进-添加EffectiveSEModule注意力机制

1、在yolov5/models下面新建一个EffectiveSEModule.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import torch from torch import nn as nn from timm.models.layers.create_act import create_act_layerclass EffectiveSEModule(nn.Module):def __init__…

【Leetcode】277.搜寻名人

一、题目 1、题目描述 假设你是一个专业的狗仔,参加了一个 n 人派对,其中每个人被从 0 到 n - 1 标号。在这个派对人群当中可能存在一位 “名人”。所谓 “名人” 的定义是:其他所有 n - 1 个人都认识他/她,而他/她并不认识其他任何人。 现在你想要确认这个 “名人” 是…

【Linux】基本指令收尾

文章目录 日期查找打包压缩系统信息Linux和Windows互传文件 日期 这篇是基本指令的收尾了&#xff0c;还有几个基本指令我们需要说一下 首先是Date&#xff0c;它是用来显示时间和日期 直接输入date的话显示是有点不好看的&#xff0c;所以我们可以根据自己的喜欢加上分隔符&…

java垃圾回收GC过程

GC&#xff08;Gabage Collection&#xff09; 用于回收堆中的垃圾数据 清理方法 1.标记-清理 对数据标记&#xff0c;然后清理 缺点&#xff1a;容易产生内存碎片 2.标记-整理 对标记后的数据清理&#xff0c;剩下数据前移 缺点&#xff1a;每次清理后数据都要迁移&#xff0…