C++相关概念和易错语法(26)(decltype、nullptr、左值和右值、移动构造和移动赋值)

news2024/11/24 6:17:33

1.decltype

“decltype(表达式) 变量名”可以定义变量,这个变量的类型是()括号内表达式的类型,注意这个表达式不会执行,只会推导这个表达式的类型,这点和sizeof一样

2.nullptr

根据#define NULL 0,可知NULL会被预处理为0,在C++的重载函数中,NULL会导致歧义

nullptr的出现就是为了解决这个问题,在以后C++中最好别用NULL,防止不必要的错误

此外C++的指针不能像C那样隐式类型转换,检查很严格,需要手动强转,使用时需要额外注意

3.左值与右值

左值和右值是C++11以后引入的新的概念,区分两者的根本是左值可取地址,右值不可取地址

0,nullptr这种常量,A()这种匿名对象都不能取地址,它们叫做右值

引入了左值右值的概念后,我们对函数的传参传返回值就能更深入地分析了

传参时右值可以给左值赋值,但是引用的话必须要使用const修饰的左值引用才可以。

传参的过程可以理解为创建变量赋值的过程,即int& a = num和int& a = 1。

当“=”右边是左值时,“=”左边的左值变量会被直接赋值;当“=”右边是右值时,这个右值会先存到另一个临时对象中(右值),之后将这个右值赋值回“=”左边的变量

函数如果返回的是值,会先将这个值存到右值(临时变量)里,再将右值赋值给接收它的左值,因此引用的时候必须要使用const修饰的左值引用

对于所有的表达式,它们都会返回一个值,这个表达式的值都会先存到右值(临时变量)里,再将这个右值赋给左值变量。

上面展示的ret1和ret2在原理上一模一样,都是返回值先存在右值,再返回结果。

总结:(1)对于所有传参过程都需要理解为创建变量的过程,类似int a = num

当创建的变量a是非左值引用时,“=”右边如果是左值的话就会被直接赋值给这个创建的变量,如果是右值的话,会先这个值存到临时变量(右值)里,再赋值回去

当创建的变量a是左值引用时,“=”右边如果是左值的话会直接取别名,而如果是右值的话就需要在左值引用前用const修饰。

(2)对于所有的表达式返回值创建变量的过程,如int a = num

当创建的变量a是非左值引用时,“=”右边如果是左值的话就会被直接赋值给这个创建的变量,如果是右值的话,会先这个值存到临时变量(右值)里,再赋值回去,所有函数的非引用返回值都是右值,就算返回值是一个变量的形式

当创建的变量a是左值引用时,“=”右边如果是左值的话会直接取别名,而如果是右值的话就需要在左值引用前用const修饰。

其实传参过程的int a = num也是表达式返回值创建变量的过程,需要仔细体会

4.右值引用

(1)右值的基本用法

右值也有分类:纯右值,内置类型右值,包括常量值1、2、3;将亡值,类类型的右值,比如匿名对象A()

右值引用是给右值取别名,int&& r = x + y,跟左值引用对立。

const int& a = 0;虽然也能取别名,但这只是兼容写法,保证不会发生权限越界,并且在功能上非常有限

这其实很好理解,用const修饰的左值引用去引用右值,虽然通过这种方式我们依然可以给左值取别名,且这个变量r本身也是左值,但它的性质其实已经和右值没有区别了,如const int&可以说和右值在性质上没区别了,都是只能只读。

但是右值引用就不是了,简单来说它更像是将一个右值强转为左值,当我们修改强转后的左值变量,就可以去修改原来的这个右值。从另一个角度上来说,右值引用可以简单理解为是一种合法化的权限扩大,当我们使用const int&来适应右值时就是为了避免修改右值,而右值引用的独特之处又恰好在于可以扩增右值的修改权限,这也是右值引用存在的最大原因。我们也会发现,const int&&几乎没有任何意义,因为右值引用的存在就是为了修改右值的权限,加了const就相当于丢失了核心作用。

(2)左值引用引用右值

前面已经强调了,当左值引用引用左值时,就是直接取别名,保证类型一致,注意const int也叫左值,因为可以取地址,只是在取别名时保证类型相同即可。左值引用引用右值时,要限制访问权限,右值不可修改,所以在前面加上const,也能给右值取别名。

(3)右值引用引用左值

右值引用引用左值同样需要特殊处理,即move(左值),它本质上就是一个强转,将左值强转为右值,即move(左值)这个表达式的返回值和1,2,3一样都是右值,右值引用本身又相当于一次强转,再将这个右值转为左值。如int&& r = move(a)就是给a取了一个别名,a是左值,move(a)是右值,而r又是左值且为a的别名

(4)底层实现

左值引用、右值引用的底层都要开空间,实质上是传递指针,通过指针来修改值,而不是真的取别名,在汇编层面没有名字的概念,也更不存在取别名的操作,我们也可以理解为什么很多情况都推荐使用引用,因为指针的大小是固定的,都只有4/8个字节,在操作一些占用大的对象时开销更小。我们也能从中知道,所谓的move(),本质上也仅仅是为了通过语法编译。因此直接使用强转也是可以通过编译的。

(5)右值引用的意义

右值引用和左值引用最大的区别在于右值引用相当于把右值变成一个左值,是一个合法化的提权,当我们通过int&& rr定义时,就相当于将这个右值取了个左值别名,当修改rr时,我们就可以修改这个右值。而const int&只能兼容右值取别名,虽然是左值引用,但它的性质和操作右值没有区别。

其次右值引用相当于一个标志,引用的对象(右值)的作用域极小和生命周期极短,如A(),Fun(),1,2,3的作用域都只有那一行,出了那一行就会销毁。那么我们是否可以借助右值的特性,先使用右值引用给这个右值取一个别名(左值),通过这个别名,再使用swap,将我们需要的右值存储的数据和不需要的数据交换,这样右值里存的就是我们不需要的数据,当出生命周期后会自动销毁,而被交换的有用的数据就被我们保存了下来。这就是移动构造和移动赋值的根本原理,同时利用了右值短生命周期的特性以及右值引用强制提权的特性。

5.移动构造和移动赋值

(1)基本使用

下面是移动构造的一个模拟的场景

当拷贝时,如果拷贝的对象是一个临时对象时就会直接调用移动构造,这样会减少开销。当传值返回时,就会直接调用移动构造,这使得函数栈帧里面创建的值返回时能够实现数据交换,将有用的数据交换到外部变量,销毁时带走的是无用数据。

注意所有非引用的函数返回值都是右值,会隐式move,也可以手动move,不过没必要

最激进的优化是函数里的变量为main函数里返回值的别名,连移动都用不上,但不是所有编译器都会这么优化

移动赋值也是如此,在使用临时变量来赋值时,就会直接调用移动赋值,将有用的数据通过更快捷的方式交换回来,同时将无用数据交给这个临时变量,当执行下一行代码前这个临时对象会去调用析构函数,也不会造成内存泄漏

这几乎是完美的处理方式,可以极大降低程序的赋值、拷贝开销,特别是对于一些数据量大的对象,只需要交换几个指针就能完成整个对象数据的交换,在大型项目中更好用。

所有STL的容器都提供了移动构造和移动赋值,我们可以直接使用且没有任何学习成本,编译器会自动匹配合适的函数,只要是将亡值就会自动匹配到更合适的移动构造和移动赋值。

(2)默认移动构造和移动赋值
深拷贝的类,移动构造才有意义,因为移动构造移动赋值本质上是针对拥有大量数据的对象拷贝赋值进行简化操作,这往往意味着移动构造、移动赋值和深拷贝绑定,只有当容器里有使用指针管理大量数据时才有实现移动构造的必要,这也意味着构造、赋值、析构函数都要实现。

具体规则:在都不写移动构造、析构、拷贝构造、赋值重载时生成默认移动构造和移动赋值,内置类型按字节拷贝,自定义类型调它的移动构造。这个很好理解,析构,拷贝、赋值共5个成员函数一体,要么都写,要么编译器生成,规则的制订很符合实际需要。在使用中我们也不需要特别注意这条规则,这5个函数的相互关联性,只需要注意深拷贝时写一个移动版本就可以了。

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

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

相关文章

搭建GAN对抗生成网络进行图像模态转换

生成对抗网络(Generative Adversarial Networks, GANs)是一种强大的生成模型,它可以通过学习训练数据的分布来生成新的样本。在医学图像处理中,GANs被广泛用于图像模态转换,例如从MRI到CT的转换,这对于临床…

【牛客】两个字符串之间的最短距离

🎗️ 主页:小夜时雨 🎗️专栏:算法题 🎗️如何活着,是我找寻的方向 目录 1. 题目解析2. 代码 1. 题目解析 题目链接: https://www.nowcoder.com/practice/2c6a0a8e1d20492f92941400036e0890 本道题是个模版…

uniapp+vue3的双向渐变

在App.vue中 <style lang"scss">/*每个页面公共css */ import common/style/common-style.scss </style> 在common-style.scss中 //全局双向渐变 .pageColor{background:linear-gradient(to bottom,rgba(0,0,0,0),#fff 400rpx),//到400rpx才做白色渐变…

解锁NGINX---SSL:打造安全、高效的网站加密体验

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

Spring Boot整合Sentry

Spring Boot整合Sentry Sentry搭建Sentry中新建项目集成SpringBoot1. 添加依赖2. 配置Sentry4. 日志集成&#xff08;可选&#xff09;5. 测试Sentry集成6. 配置实时告警配置Alert Settings配置警报规则 发送消息服务代码参照文档 Sentry 是一个日志平台&#xff0c;分为客户端…

WEB服务器的部署及优化

什么是 www&#xff1f; www 是 world wide web 的缩写&#xff0c;及万维网&#xff0c;也就是全球信息广播的意思 通常说的上网就是使用 www 来查询用户所需要的信息。 www 可以结合文字、图形、影像以及声音等多媒体&#xff0c;超链接的方式将信息以 Internet 传递到世界…

记录一下QGIS栅格操作-植被NDVI指数计算

记录一下QGIS栅格操作-植被NDVI指数计算 以计算植被NDVI指数为例&#xff0c;介绍QGIS栅格的相关操作以及应用。 NDVI 数据简介及下载 下载&#xff1a; 地理空间数据云https://www.gscloud.cn/search 选择波段4&#xff08;可见光红色波段&#xff09;和波段5&#xff08;近…

VS2022 - 制作自己的C#类库dll,并输出Unity识别的pdb调试信息文件

然后编写库代码&#xff0c;设置dll生成目录 *** 输出unity可以识别的pdb调试信息文件 *** 右键项目-属性-生成-高级-调试信息&#xff1a;可移植(Portable PDB) 这是因为Unity只能识别MDB和Portable PDB文件 这样设置后&#xff0c;把dll和pdb文件放入到Unity中同文件夹下&…

金融帝国实验室(Capitalism Lab)官方技术支持中文汉化包_v4.09

<FCT汉化小组>Vol.001号作品 ————————————— ◎ 作品名称&#xff1a;金融帝国实验室&#xff08;Capitalism Lab&#xff09;官方中文汉化包 ◎ 制作发布&#xff1a;FCT汉化小组 ◎ 发布版本&#xff1a;CapLab Simplified Chinese loc v4.09 ◎ 发布时…

django学习入门系列之第十点《初识 django》

文章目录 django初识django1 安装django2 创建django项目3 默认项目文件介绍4 APP 往期回顾 django Python知识点:函数、面向对象。前端开发: HTML、CSS、JavaScript、jQuery、BootStrap。MySQL数据库Python的Web框架Flask&#xff0c;自身短小精悍第三方组件。Django&#xf…

Linux驱动开发基础(SR501人体红外模块)

所学来自百问网 目录 1.模块简介 2.原理图及接线 3.设备树修改 4.驱动程序 5.应用程序 6.makefile 7.编译运行 1.模块简介 人体都有恒定的体温&#xff0c;一般在37度&#xff0c;所以会发出特定波长10uM左右的红外线&#xff0c;被动式红外探头就是靠探测人体发射的1…

Kafka·概述

概览 Producer 生产者发送消息给broker&#xff0c;并不是生成一条消息后立刻发送&#xff0c;而是积攒多条后&#xff0c;批量发送到broker。可以通过配置参数batch.size&#xff08;单位字节&#xff09;调整积攒多少后发送 Consumer Topic 消息的分类 当Producer发送指定…

沾包问题,wireshark和netstat的使用

一.沾包 TCP是一个面向字节流的传输层协议。“流” 意味着 TCP 所传输的数据是没有边界的。这不同于 UDP 协议提供的是面向消息的传输服务&#xff0c;其传输的数据是有边界的。TCP 的发送方无法保证对方每次收到的都是一个完整的数据包。于是就有了粘包、拆包问题的出现。粘包…

[数据集][目标检测]电力场景输电线杆塔塔架金属锈蚀腐蚀生锈检测数据集VOC+YOLO格式1344张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1344 标注数量(xml文件个数)&#xff1a;1344 标注数量(txt文件个数)&#xff1a;1344 标注…

医学领域使用Python语言的逻辑和代码的规范

在医学领域使用Python进行数据分析和建模时&#xff0c;掌握良好的编程逻辑和代码规范非常重要。这不仅可以提高代码的质量&#xff0c;还能让其他研究人员更容易理解和复现你的工作。下面是一些关于如何编写高质量Python代码的建议&#xff1a; 代码组织 模块化&#xff1a;…

软件设计师教程(第5版)第5章 软件工程基础知识(更新中)

5.1 软件工程概述 【软件工程】是指应用计算机科学、数学及管理科学等原理,以工程化的原则和方法来解决软件问题的工程&#xff0c;其目的是提高软件生产率、提高软件质量、降低软件成本。P239 5.1.1 计算机软件 计算机软件是指计算机系统中的【程序】及其【文档】。P240 【…

android13 隐藏状态栏里面的背光调节 隐藏下拉栏背光调节

总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题分析 3.修改方法 4.编译运行 5.彩蛋 1.前言 隐藏下拉栏里面的背光调节,禁止用户在这里调节背光亮度。 2.问题分析 我们找到对应的布局,然后在里面隐藏掉。 使用之前文章介绍的布局查找工具,查找亮度条id id/bri…

驱动开发系列11 - Linux Graphics 图形栈概述(二)

目录 一:GPU 和 硬件 现代 GPU 功能概览: 硬件结构: 屏幕驱动: 屏幕连接器: 屏幕 CRT 控制器: CPU与GPU通信: 现代主机通信总线介绍: 通信方法: GPU 编程:通过 MMIO 访问寄存器 CPU 和 GPU 内存请求路由: GPU 可访问的内存区域: GTT/GART 是 CPU 与 GPU 共享的通信缓…

谷粒商城实战笔记-254-商城业务-消息队列-Direct-Exchange

文章目录 一&#xff0c;252-商城业务-消息队列-Direct-Exchange1&#xff0c;创建4个队列2&#xff0c;exchange绑定queue3&#xff0c;发送消息 二&#xff0c;253-商城业务-消息队列-Fanout-Exchange1&#xff0c;创建一个type为fanout的exchange2&#xff0c;给这个exchang…

开源的即时聊天解决方案Papercups

Papercups&#xff1a;让聊天支持变得简单、私密、实时。 - 精选真开源&#xff0c;释放新价值。 概览 Papercups是一款开源的实时客户支持工具&#xff0c;它使用Elixir语言构建&#xff0c;为注重客户数据隐私和安全性的公司提供了一个自托管的解决方案。这款工具的设计理念…