当前位置: 首页 > news >正文

拷贝控制总结

1.拷贝、值与销毁:

拷贝构造函数:如果一个构造函数的第一个参数是自身类类型的引用,且其他(如果有的话)参数都有默认实参,则此构造函数叫做拷贝构造函数;如果我们没有为类定义一个拷贝构造函数,那么编译器就会为我们定义一个合成拷贝构造函数。

拷贝初始化:

直接初始化与拷贝初始化的差异:当使用直接初始化时,我们实际上是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。当我们使用拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,而不是直接将值赋予正在创建的对象,是先构造一个临时对象,然后将其拷贝到正在创建的对象,如果需要的话还要进行类型转换。

拷贝初始化不仅在我们用 “ = ”定义变量时会发生,在下列情况中也会发生:

        (1)将一个对象作为实参传递给一个非引用类型的形参;

        (2)从一个返回类型为非引用类型的函数返回一个对象;

        (3)用花括号列表初始化一个数组中的元素或一个聚合类中的成员。

与拷贝构造函数对应的就是拷贝赋值运算符:

赋值运算符通常返回一个指向其左侧运算对像的引用;

合成拷贝赋值运算符:如果类未定义一个拷贝赋值运算符,那么编译器就会为其生成一个合成拷贝复制运算符。

析构函数:析构函数是类的一个成员函数,无返回值,不接受参数,因其不接受参数,所以其不能被重载,对于一个给定类,只会有唯一一个析构函数。

在一个析构函数中,不存在类似构造函数中初始化列表中的东西来控制成员如何销毁,析构部分是隐式的。成员销毁时发生什么完全依赖于成员的类型。销毁类类型的成员需要执行成员自己的析构函数。内置类型没有析构函数,因此销毁内置类型成员什么也不需要做。隐式销毁一个内置指针类型的成员不会delete它所指向的对象。与普通指针不同,智能指针是类类型,所以具有析构函数。因此智能指针成员会在析构阶段自动销毁。

什么时候回调用析构函数(认为非常重要):

无论何时想,一个对象被销毁,就会自动调用其析构函数:

        (1)变量在离开其作用域时被销毁;

      (2)当一个对象被销毁时,其成员被销毁;

      (3)容器被销毁时,其元素被销毁;

        (4) 对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁;

      (5)对于临时对象,当创建它的完整表达式结束时被销毁。  

同样为定义析构函数,编译器也会自动生成合成析构函数。

三 / 五法则:有三个基本操作可以控制类的拷贝操作:拷贝构造函数、拷贝赋值运算符、析构函数。在新标准下,一个类还可以定义一个移动构造函数和一个移动赋值运算符。

当我们决定一个类是否要定义他自己版本的拷贝控制成员时,一个基本原则是首先确定这个类是否需要一个析构函数。如果需要,则几乎可以肯定也需要一个拷贝构造函数和一个拷贝赋值运算符。

合成的析构函数不会delete一个指针数据成员(指针所指对象),因此此类型的类需要定义一个析构函数。需要拷贝操作的类也需要赋值操作,反之亦然。

我们可以通过使用=default定义拷贝控制成员来显示的要求编译器生成合成的版本。当我们在类内用=default修饰成员的声明时,合成的函数将隐式的声明为内联的(像任何其他类内声明一样),如果我们不希望合成的成员是内联函数,应该只对成员的类外定义使用=default。我们只能对具有合成版本的成员函数使用=default(即,默认构造函数或拷贝控制成员)。

定义删除的函数:我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数(delete function)来阻止拷贝。删除的函数:我们虽然定义了它,但不能以任何方式使用它。在函数的参数列表后面加上=delete来指出我们希望将它定义为删除的,必须出现在该函数的第一次声明时。

注意析构函数是不能定义成删除的成员:如果析构函数被删除,就无法销毁此类型的对象了,对于一个删除了析构函数的类,编译器将不允许定义该类型的变量或创建该类型的临时对象,但是可以动态分配这种类型的对象,不能释放指向该类型动态分配对象的指针。

对于某些类来说,编译器将这些合成的成员定义为删除的函数:

1、如果类的某个成员的析构函数是删除的或者不可访问的(例如是private的),则类的合成析构函数被定义为删除的;

2、如果类的某个成员的拷贝构造函数是删除的或不可访问的,则类的合成拷贝构造函数被定义为删除的。如果类的某个成员的析构函数是删除的或者不可访问的,则该类合成的拷贝构造函数也被定义为删除的;

3、如果类的某个成员的拷贝赋值运算符是删除的或不可访问的,或者类有一个const的或引用成员,则类的合成拷贝赋值运算符被定义为删除的;

4、如果类的某个成员的析构函数是删除的或者不可访问的,或是类有一个引用成员,它没有类内初始器,或是有一个const成员,它没有类内初始器且其类型未显示定义默认构造函数,则该类的默认构造函数被定义为删除的。

综上:如果一个类内成员的“三’是缺失的或不可访问的则类的相关合成的“三”会被定义为删除的。

类内成员函数可以只声明不定义,可以将拷贝赋值运算符和拷贝构造函数声明为private的,当未定义时可以阻止任何拷贝该类对象的企图:试图拷贝对象的用户代码将在编译阶段被标记为错误;成员函数或友元函数中的拷贝操作将会导致链接时错误。

2.拷贝控制与资源管理:

当编写赋值运算符时,有两点需要记住:

1、如果将一个对象赋予它自身,赋值运算符必须能正确工作(一个好的办法是在销毁左侧运算对象之前,拷贝右侧运算对象);

2、大多数赋值运算符组合了析构函数和拷贝构造函数的工作。

当编写一个赋值运算符时,一个好的模式是将右侧运算对象拷贝到一个局部临时对象中。当拷贝完成后,销毁左侧运算对象的现有成员就是安全的了。一旦左侧运算对象的资源被销毁,就只剩下将数据从临时对象拷贝到左侧运算对象的成员了。

3.交换操作:

对于拷贝控制成员不同,swap并不是必要的,但是 对于分配了资源的类,定义swap可能是一种很重要的优化手段。本节主要内容为swap的定义与应用。

4.拷贝控制实例:

本节为Message类和Folder类的定义来处理消息,Message为一个存放信息的类,Folder为保存Message的类,Message类也有一个变量存放本信息存放于那些Folder中。我认为最难得一点就是同步处理Message和Folder。

5.动态内存管理类:

某些类需要在运行时分配可变大小的内存空间。这种类通常可以使用标准库容器来保存它们的数据,但是某些类需要自己进行内存分配,这些类一般来说必须定义自己的拷贝控制成员来管理所分配的内存。默认定义三个指针成员指向其元素所使用的内存:elements,指向分配的内存中的首元素;first_free,指向最后一个实际元素之后的位置;cap,指向分配的内存末尾之后的位置。经常能用到的算法:construct(p,args)(p必须是一个类型为调用对象所存对象相同的指针,指向一块原始内存;args被传递给类型为调用对象的构造函数,用来在p所指的内存中构造一个对象),allocate(n)(分配一段原始的、未构造的内存,保存 n 个类型为调用对象所存对象类型的对像),chk_n_alloc()确保有空间容纳新元素

对于移动构造函数move和reallocate比较难懂与重要:

首先:move是一个标准库函数,构造移动构造函数时,需要调用move来表示希望使用string或其他类型的移动构造函数,如果漏掉了move的调用,将会使用string或其他类型的拷贝构造函数,当我们使用move时,直接调用std::move而不是move。

6.对象移动:

右值引用:右值引用有一个重要的性质:只能绑定到一个将要销毁的对象,因此,我们可以自由的将一个右值引用的资源“移动”到另一个对象中。

左值和右值是表达式的属性,一些表达式生成或要求左值,而另一些则生成或要求右值。一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。右值引用可以将其绑定到要求转换的表达式(类型,形式)、字面值常量、或是右值的表达式,跟左值引用有着完全相反的绑定特性,但是不能将一个右值引用直接绑定到一个左值上。

由于右值只能绑定到临时对象:1、所引用的对象将要销毁;2、该对象没有其他用户。

这两个特性意味着:使用右值引用的代码可以自由地接管所引用的对象的资源。

变量是左值:可以看作一个只有运算对象而没有运算符的表达式;

变量是左值,因此我们不能直接将一个右值引用绑定到一个变量上,即使这个变量是右值引用类型也不行。

虽然不能直接将一个右值引用绑定到一定左值上,但是我们可以显示地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的标准库函数来获得绑定到左值上的右值引用。move告诉编译器:我们有一个左值,但是我们希望像一个右值一样处理它。我们必须认识到,调用move就意味着承诺:除了对此左值赋值或销毁它外,我们将不在使用它。在调用move函数后,我们不能对移后源对象的值做任何假设。与大多数标准库函数的使用不同,对于move不通过using声明,直接调用std::move而不是move。关于其具体工作机制将会在第十六章(模版与泛型编程)总结。

对于移动操作,除非标准库直到我们的移动构造函数不会抛出异常,否则他它会认为移动我们的类对象时可能会抛出异常,并且为了处理这种可能性而坐一些额外的工作,通过在形参列表后写关键字“noexcept”来通知标准库我们的构造函数不会抛出任何异常。不抛出异常的移动构造函数和移动赋值运算符必须标记为“noexcept”。因为标准库容器能对异常发生时其自身的行为提供保障。

与拷贝操作不同编译器根本不会为某些类合成移动构造函数。特别是,如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或者析构函数,编译器将不会为他合成移动构造函数和移动赋值运算符了。只有当一个类没有定义任何自己版本的拷贝控制函数,且类的每个非static数据成员都可以移动时,编译器才会为他合成移动构造函数或者移动赋值运算符。编译器可以移动内置类型的成员。如果一个成员是类类型,且该类有对应的移动操作,编译器也能移动这个成员。

与拷贝操作不同,引动操作永远不会隐式地定义为删除的函数。但是如果我们显示地要求编译器生成=default的移动操作,且编译器不能移动所有成员时,编译器会将移动操作定义为删除的操作。

定义了一个移动构造函数或移动赋值运算符的类必须也定义自己的拷贝操作,否则,这些成员默认地被定义为删除的。

所有五个拷贝控制成员应该看作一个整体:一般来说,如果一个类定义了任何一个拷贝操作,它就应该定义所有五个操作。

移动迭代器:解引用运算符生成一个右值引用,通过调用标准库的make_move_iterator函数将一个普通迭代器转换为一个移动迭代器。

对于移动操作的使用要谨慎。

右值和左值引用成员函数:我们指出this的左值/右值属性的的方式与定义const成员函数相同,即在参数列表后放置一个引用限定符,对于&限定的函数,我们只能将它用于左值;对于&&限定的函数,只能用于右值。一个函数可以同时用const和引用限定。在此情况下,引用限定符必须跟在const之后。const和引用限定符都可以用来区分重载,但,如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符。明天更新下一章节。

相关文章:

拷贝控制总结

1.拷贝、值与销毁: 拷贝构造函数:如果一个构造函数的第一个参数是自身类类型的引用,且其他(如果有的话)参数都有默认实参,则此构造函数叫做拷贝构造函数;如果我们没有为类定义一个拷贝构造函数…...

无重复字符串的最长子串

题目描述:给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度。 第一次提交记录 class Solution:def lengthOfLongestSubstring(self, s: str) -> int:if not s:return 0lookup set()left res 0for right in range(len(s)):while s…...

javaScript Object.hasOwn()的用法

Object.hasOwn() 如果指定的对象自身有指定的属性,则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在,该方法返回 false。 备注: Object.hasOwn() 旨在取代 Object.prototype.hasOwnProperty()。 **语法:**Objec…...

MINI2440 开发板 给他干出来了

环境是ubuntu14.04。不要问我为什么是这个版本,因为之前的ubuntu12.04 环境干不出来,你去试试就知道了!各种资源包下载不下来。 输入启动参数: 进入MINI2440:别说心里一万个开心,启动完成,输入p…...

上海人工智能实验室的书生·浦语大模型学习笔记(第二期第三课——上篇)

书生浦语是上海人工智能实验室和商汤科技联合研发的一款大模型,这次有机会参与试用,特记录每次学习情况。 一、课程笔记 本次学习的是RAG(Retrieval Augmented Generation)技术,它是通过检索与用户输入相关的信息片段…...

前端小白的学习之路(Vue2 三)

提示:学习vue2的第三天,笔记记录:生命周期,组件(注册,传值) 目录 一、生命周期 二、组件 1.注册组件 1)全局注册 2)局部注册 2.组件传值 1)父传子 2)子传父 3)兄弟传值 一…...

ChatGPT 之优势与缺陷

原文: 译者:飞龙 协议:CC BY-NC-SA 4.0 介绍 欢迎来到《ChatGPT:好的、坏的和丑陋的》。在本书中,我们踏上了探索 ChatGPT 多面世界的旅程,这是由 OpenAI 开发的先进自然语言处理模型。随着 ChatGPT 和类似…...

python爬虫———post请求方式(第十四天)

🎈🎈作者主页: 喔的嘛呀🎈🎈 🎈🎈所属专栏:python爬虫学习🎈🎈 ✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天…...

51蓝桥杯之DS18B20

DS18B20 基础知识 代码流程实现 将官方提供例程文件添加到工程中 添加onewire.c文件到keil4里面 一些代码补充知识 代码 #include "reg52.h" #include "onewire.h" #include "absacc.h" unsigned char num[10]{0xc0,0xf9,0xa4,0xb0,0x99,…...

TiDB 组件 GC 原理及常见问题

本文详细介绍了 TiDB 的 Garbage Collection(GC)机制及其在 TiDB 组件中的实现原理和常见问题排查方法。 TiDB 底层使用单机存储引擎 RocksDB,并通过 MVCC 机制,基于 RocksDB 实现了分布式存储引擎 TiKV,以支持高可用分…...

【c++】STl-list使用list模拟实现

主页:醋溜马桶圈-CSDN博客 专栏:c_醋溜马桶圈的博客-CSDN博客 gitee:mnxcc (mnxcc) - Gitee.com 目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 …...

号卡极团分销管理系统 index.php SQL注入漏洞复现

0x01 产品简介 号卡极团分销管理系统,同步对接多平台,同步订单信息,支持敢探号一键上架,首页多套UI+商品下单页多套模板,订单查询支持实时物流信息、支持代理商自定义域名、泛域名绑定,内置敢探号、172平台、号氪云平台第三方接口以及号卡网同系统对接! 0x02 漏洞概述…...

内核驱动更新

1.声明我们是开源的 .c 文件末尾加上 2.在Kconfig里面修改设备,bool(双态)-----》tristate(三态) 3.进入menuconfig修改为M 4.编译内核 make modules 也许你会看到一个 .ko 文件 5.复制到根目录文件下 在板子…...

故障诊断 | 一文解决,PLS偏最小二乘法的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,PLS偏最小二乘法的故障诊断(Matlab) 模型描述 偏最小二乘法(Partial Least Squares, PLS)是一种统计建模方法,用于建立变量之间的线性关系模型。它是对多元线性回归方法的扩展,特别适用于处理高维数据和具有多重共线性的数据集。…...

我为什么选择成为程序员?

前言: 我选择成为程序员不是兴趣所在,也不是为了职业发展,全是生活所迫! 第一章:那年,我双手插兜,对外面的世界一无所知 时间回到2009年,时间过得真快啊,一下就是15年前…...

Open CASCADE学习|统计形状拓扑数量

边界表示法(Boundary Representation,简称B-Rep)是几何造型中最成熟、无二义的表示法。它主要用于描述物体的几何信息和拓扑信息。在边界表示法中,一个实体(Solid)由一组封闭的面(Face&#xff…...

LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

题目四:接雨水(No. 43) 题目链接:https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度:困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…...

常用的深度学习自动标注软件

0. 简介 自动标注软件是一个非常节省人力资源的操作,而随着深度学习的发展,这些自动化标定软件也越来越多。本文章将会着重介绍其中比较经典的自动标注软件 1. AutoLabelImg AutoLabelImg 除了labelimg的初始功能外,额外包含十多种辅助标注…...

选择程序员是为什么?

本章节是关于为什么会选择一名程序员的经验分享 首先,我为什么会选择这个方向,可能是因为钱多,学东西不就是为了赚钱嘛?这是一点,不过最让我接收这个行业的是好奇世界的新大陆,可以简单的说就是&#xff0c…...

线程池参数如何设置

线程池参数设置 hello丫,各位小伙伴们,好久不见了! 下面,我们先来复习一下线程池的参数 1、线程池参数有哪些? corePoolSize(核心线程数):线程池中的常驻核心线程数。即使这些线程…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则&#xf…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...