【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;}

左值引用的作用和意义

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

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

    //MyStringstring& 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中的右值引用和移动构造。如果觉得文章还不错的话,可以三连支持一下,个人主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的三连支持就是我前进的动力!

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

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

相关文章

C++ 学习系列 -- std::function 与 std::bind

一 std::function 与 std::bind 的介绍 1. std::function std::function 是 c 11 的新特性 &#xff0c;包含在头文件<functional>中&#xff0c;为了更方便的调用函数而引入。 std::function 是一个函数包装器&#xff08;function wrapper&#xff09;&#xff0c;…

CHAPTER 11: 《DESIGN A NEWS FEED SYSTEM》 第 11 章:《设计新闻系统》

在本章中&#xff0c;您将被要求设计一个新闻提要系统。什幺是新闻系统&#xff1f;根据Facebook 页面&#xff0c;“News feed 是中间不断更新的故事列表。您的主页。动态消息包括状态更新、照片、视频、链接、应用活动记录和喜欢您在 Facebook 上关注的人、页面和群组“[1]。…

Mysql运维篇(三) MySQL数据库分库分表方案

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人&#xff0c;如有侵权请留言&#xff0c;我及时删除。 一、前言 关系型数据库本身比较容易成为系统瓶颈&#xff0c;单机存储容量、连接数、处理能力都有限。当单表的数…

磁盘的分区与文件系统的认识

磁盘的认识 了解磁盘的结构&#xff1a; 1、盘片 硬盘首先会有多个盘片构成&#xff0c;类似很多个独立的光盘合并在一起&#xff0c;每个盘片都有2个面&#xff0c;每个盘片都有一个对应的磁头&#xff0c;我们的磁头横移和盘面的旋转就可以读写到盘面的每一个位置&#xff0c…

08. 面向对象编程(二)

目录 1、前言 2、多重继承 2.1、潜在的问题 3、Property 4、staticmethod 5、运算符重载 5.1、加法运算符 的重载 5.2、字符串表示运算符 str() 的重载 5.3、索引运算符 [] 的重载 6、小结 1、前言 上一篇文章中&#xff0c;我们介绍了面向对象编程的类和实例&…

v35-36.problems

1.size of&#xff08;&#xff09;运算符 根据c语言标准规范&#xff0c;Size of&#xff08;&#xff09; 里面的操作数如果不是可变长度数组类型 &#xff0c;那么此操作数就不会被执行 &#xff01;

x-cmd pkg | skate - 个人键值对存储工具

目录 简介用户首次快速实验指南功能特点竞品和相关作品进一步探索 简介 skate 是个人键值对存储工具&#xff0c;具备数据加密、云端数据备份以及多设备同步等功能。 它由 Charm 团队开发&#xff0c;借用 Charm Cloud 向用户提供一种快捷的方式来保存和检索各种数据&#xf…

零基础学习【Linux】这一篇就够了

Linux学习目录 1. Linux简介1-1. Linux系统版本 2. Linux安装2-1. 安装方式2-2. Vmware安装2-3. Linux安装2-4. Vmware克隆2-5. Vmware快照2-6. 连接工具安装 3. Linux常用命令4. Linux软件安装5. 项目部署 1. Linux简介 1-1. Linux系统版本 Linux系统分为内核版和发行版 内核…

「斗破年番」大紫研爆虐六星斗皇,佛怒火连回归,异火焚烧分身

Hello,小伙伴们&#xff0c;我是拾荒君。 国漫《斗破苍穹年番》第80期超前爆料&#xff0c;据透露韩枫以海心焰这一异火贡献给了慕骨老人&#xff0c;换取了一具斗宗躯体。通过灵魂融入&#xff0c;他成功达到了斗宗四星阶段。而与小医仙对决的莫天行&#xff0c;尽管两人实力…

Java设计模式详解-更新中

收藏和关注的同时&#xff0c;请也关注 公众号 “IT技术馆” 各位大家好&#xff0c;从今天开始&#xff0c;作者开始整理 《JAVA软件设计模式&#xff08;GOF&#xff09;》 专栏。请各位多多关注&#xff01; 该专栏是根据作者的技术经验和设计模式的了解&#xff0c;进行详…

从零开始c++精讲:第三篇——内存管理

文章目录 一、C/C内存分布二、C语言中动态内存管理方式:malloc/calloc/realloc/free三、C中动态内存管理四、operator new与operator delete函数4.1 operator new与operator delete函数&#xff08;重点&#xff09; 五、new和delete的实现原理5.1内置类型5.2 自定义类型 六、定…

代码随想录二刷 | 回溯 | 组合优化

代码随想录二刷 &#xff5c; 回溯 &#xff5c; 组合优化 剪枝优化 剪枝优化 在遍历的过程中有如下代码&#xff1a; for (int i startIndex; i < n; i) {path.pop_back();backtracking(n, k, i 1);path.pop_back(); }n 4&#xff0c;k 4的话&#xff0c;那么第一层f…

打折:阿里云国外服务器价格购买优惠活动

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…

idea 折叠某段代码 这段特定某段代码

如何折叠IntelliJ IDEA代码片段_w3cschool ctrlALTT

圆的参数方程是如何推导的?

圆的参数方程是如何推导的? 1. 圆的三种参数表示2. 三角函数万能公式3. 回到圆的参数方程1. 圆的三种参数表示 已知圆的第一种参数方程为: x 2 + y 2 = r x^2+y^2=r x2+y2=r   圆的图像如下: 通过上图,不难理解,圆的参数方程还可以用三角函数表示,也就是第二种参数表…

计算机毕业设计 | SpringBoot学生成绩管理系统(附源码)

1&#xff0c; 概述 1.1 课题背景 开发一个学生成绩管理系统&#xff0c;采用计算机对学生成绩进行处理&#xff0c;进一步提高了办学效益和现代化水平。为广大教师和学生提高工作效率&#xff0c;实现学生成绩信息管理工作流程的系统化、规范化和自动化。现在我国中学的学生…

基于STM32CubeMX创建FreeRTOS—以STM32F429为例

目录 1. 实验任务 2. 使用STM32CubeMX创建基础工程 2.1 打开STM32CubeMX 2.2 创建新项目 2.3 时钟设置 2.5 修改时钟基准&#xff0c;打开串行调试 2.6 配置串口 2.7 配置状态指示灯 2.8 FreeRTOS 2.9 配置工程输出项 3. 代码编辑 3.1 printf重映射 3.1.1 使用ARM…

【JavaEE】网络初识 (IP地址, 端口号, 协议, 封装和分用)

文章目录 前言网络通信基础一.IP地址概念格式特殊IP 二.端口号概念注意事项 三.协议概念知名协议的默认端口五元组协议分层OSI七层模型TCP/IP五层模型 四.封装和分用 前言 本章来介绍一下网络中的一些基本概念, 例如 : IP地址, 端口号, 协议, 协议分层, 封装, 分用等等. 网络…

计组原理:系统概论与基本组成

系统概论与基本组成 系统概论硬件软件 计算机系统的层次结构系统复杂性的管理方法1&#xff1a;抽象 计算机的基本组成冯诺依曼计算机系统复杂性的管理方法 2&#xff1a;&#xff08;3’Y&#xff09; 计算机的工作步骤上机前的准备&#xff1a;计算机的解题过程存储器的基本组…

AI教我学编程之C#类的实例化与访问修饰符

前言 在这篇文章中&#xff0c;我将带大家深入了解C#编程语言的核心概念&#xff0c;包括类的实例化、访问修饰符的应用&#xff0c;以及C#中不同数据类型的默认值。我会通过逐步分析和具体实例&#xff0c;详细解释如何在C#中正确创建和操作对象&#xff0c;并探讨如何通过访…