智能指针与定制删除器

定制删除器

前面我们的智能指针就是:

  1. RAII。

  2. 像指针一样。

但是我们的智能指针的析构函数就只是 delete。

// 这里简单的看一下 shared_ptr 就知道了~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}

那么如果我们想要 new[] 呢? 或者既然是 RAII 的思想,那么就应该其他的资源也可以管理,就比如说是 malloc 或者是文件文件描述符等...

所以我们还是需要定制删除器,也就是定制一个我们自己的 delete。

而定制删除器实际上也就是一个可调用对象,既可以是函数指针,也可以是仿函数,亦或者是 lambda 表达式。

我们现在看一下没有写定制删除器的智能指针,如果 new[] 会怎么办?

  • 首先,我们前面说过 new 与 delete 搭配使用,new[] 与 delete[] 搭配使用。

void test8()
{lxy::shared_ptr<A> Pa(new A[3]);
}

结果:

构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
  • 这里看到 A 的构造函数被调用的3次,说明全部都被构造好了。

  • 但是析构函数值调用的了一次,因为 shared_ptr 默认就是 delete,所以只调用了一次析构,才会导致奔溃。

下面我们就可以写我们的定制删除器,那么我们想要该类再析构的时候调用可调用对象来析构,那么当然是我们再构造的时候,或者是模板的时候就传进入一个仿函数,或者是再构造函数的时候再传入可调用对象,但是STL 库里面是再构造函数的时候传入的,所以我们也在构造函数的时候传入。

但是,如果是我们仅仅传入的话,那么当析构的时候我们不知道去那里找这个可调用对象,但是我们可以将他保存起来。

那么我们如何保存呢?我们怎么知道传进来的是一个函数,还是仿函数,还是 lambda 表达式。

其实我们可以巧妙的利用C++11 的包装器!!

我们可以给 shared_ptr 中添加一个成员变量,也就是用来记录定制删除器的变量,那么我们可以将该变量声明为一个包装器类型的一个变量,那么我们如何声明呢?

  • 因为我们再删除的时候,由于不知道 T* _ptr 是什么类型的,如果是 new[] 那么我们就需要用 delete[] 如果是 malloc 我们就需要 free ,如果是 FILE 那么我们就需要 close 所以我们可以将该函数传入一个 ptr 的指针,也就是需要管理的资源的指针,目的就是释放该管理的资源,而 shared_ptr 里面还有一个 int* 的计数器,该计数器不需要我们来管理,因为这个计数器他本身就是一个 new 出来的,我们只需要将其 delete 即可,而我们真正需要管理的就是我们想要让 shared)ptr 为我们管理的资源。

function<void(T* ptr)> _del = [](T* ptr) {delete ptr; };
  • 这里就是该成员变量的声明,我们可以在构造函数的时候给他一个定制删除器。

下面我们就改写一下 shared_ptr 改写一些并不难。

我们只需要为 shared_ptr 类添加一个构造函数,也就是可以传入定制删除器的构造函数。

        shared_ptr(T* ptr, function<void(T* ptr)> del):_ptr(ptr), _pcount(new int(1)), _del(del){}
  • 也就是我们为该类添加一个这样的函数,但是为了方便,我买也可以直接为该构造函数添加一个模板。

        template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)),_del(del){}
  • 我们可以写成这样。这里两种都是可以的。

那么上面这样改写就算结束了吗?

当然没有,我们先看下面的这段代码。

void test8()
{auto del = [](A* ptr) {delete[] ptr; };
​lxy::shared_ptr<A> Pa(new A[3], del);lxy::shared_ptr<A> Pa1(Pa);
​
}
  • 我们先看这样会不会报错。

结果:

构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
析构函数:~A()

这样我们是没有问题的,即使我们拷贝了也没有问题,但是这样真的就没有问题吗?

我们拷贝后,拷贝的对象里面的 _del 成员变量是什么,是默认的 delete 所以我们还是需要为拷贝和赋值都添加一个。

这里为了验证我们所说的是正确的,我们可以看一下拷贝后失败的案例,但是上面为什么拷贝后还是成功的呢?

因为先创建的后析构,所以这里是因为 Pa1 先析构,但是 Pa1 里面的计数器减减后并没有到0,所以没有析构,而有计数器的那个对象是后析构的,所以他析构的时候,shared_ptr里面的 T* _ptr 析构会调用我们写的定制删除器,所以没有办法,那么我们要怎么让拷贝的对象会是最后一次析构呢?我们可以把一个对象赋值给Pa 这样 Pa 所管理的资源的引用计数就会减减,而这份资源的最后一次管理权也就会交到 Pa1 手中,所以只需要等Pa1 析构的时候就是这份资源释放的时候,而这份资源是 new[] 出来的,而 Pa1 里面的定制删除器是,默认的也就是简单的 delete 所以此时析构的话就会报错。

void test8()
{auto del = [](A* ptr) {delete[] ptr; };lxy::shared_ptr<A> Pa(new A[3], del);lxy::shared_ptr<A> Pa1(Pa);lxy::shared_ptr<A> Pa2(new A[5], del);Pa = Pa2;
}
  • 这样 Pa2 给 Pa1 此时 Pa1 手中的那一份资源的管理全就会减减,然后只剩下 Pa1 手中的一份。

  • 所以再 Pa1 析构的时候就会报错。

结果:

构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
  • 这里我们看到了 只析构了一次。

那么为什么赋值也要给 del 也赋值呢?

  • 因为再赋值的时候,如果一个new[] 需要用 delete[] 释放,而一个是 new 只需要 delete 就可以释放,所以如果不将定制删除其也赋值的话,那么就会出现问题。

  • 上面这个的报错我们就不演示了。

其实有了定制删除器后,我们可以染发 shared_ptr 管理其他的资源,就不光光管理指针的,真正的实现了 RAII 的思想。

我们将打开的文件指针交给 shared_ptr 进行管理:

void test9()
{auto del1 = [](FILE* fd) {fclose(fd); };lxy::shared_ptr<FILE> Pf(fopen("test.txt", "w"), del1);fprintf(Pf.getPtr(), "%s", "hello world");
}
  • 这里为了支持我们的写入,我们需要提供一个 getPtr 因为我们写入的时候,需要文件指针,而我们需要提供getPtr 或者是将 _ptr 放成公有,才可以,这里选择提供getPtr。

结果:

hello world

这就是文件里面写入的内容。

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

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

相关文章

基于知识图谱建模、全文检索的智能知识管理库(源码)

一、项目介绍 一款全源码&#xff0c;可二开&#xff0c;可基于云部署、私有部署的企业级知识库云平台&#xff0c;一款让企业知识变为实打实的数字财富的系统&#xff0c;应用在需要进行文档整理、分类、归集、检索、分析的场景。 知识图谱提供了一种从海量文本和图像中抽取结…

2023大联盟2比赛总结

比赛链接 反思 T1 奇怪的贪心和构造题一直是我的软肋部分 T2 简单题 T3 也不难 T4 套路没学过&#xff0c;感觉还是太菜了 题解 A 考虑先给图随便染色&#xff0c;然后调整 因为每个点的度数为 3 3 3&#xff0c;所以如果有 x → u → v x\to u\to v x→u→v 的颜…

自定义类型:结构体,枚举,联合 (2)

2. 位段 位段的出现就是为了节省空间。 2.1 什么是位段 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int…

Pygame中将鼠标形状设置为图片2-2

3 编写主程序 在主程序中&#xff0c;首先创建屏幕并且完成一些准备工作&#xff0c;之后在while循环中不断更新sprite实例即可。 3.1 创建屏幕及准备工作 创建屏幕及准备工作的代码如图5所示。 图5 创建屏幕及准备工作 其中&#xff0c;第20行代码调用pygame.mouse模块中的…

Hadoop3教程(五):NameNode和SecondaryNameNode

文章目录 &#xff08;59&#xff09;NN和2NN的工作机制&#xff08;60&#xff09;FsImage镜像文件&#xff08;61&#xff09;Edits编辑日志&#xff08;62&#xff09;Checkpoint时间设置参考文献 &#xff08;59&#xff09;NN和2NN的工作机制 NameNode的数据是存储在磁盘…

思维模型 正/反 木桶理论

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。 1 正/反 木桶理论的应用 1.1 木桶理论的应用 1.1.1 正木桶理论在考试中的应用 小明是一名理科高中生&#xff0c;他在学习过程中发现自己在数理化方面表现较好&#xff0c;但在语文和英…

【02】基础知识:React - jsx语法规则

一、jsx 简介 全称为JavaScript XML&#xff0c;是 react 定义的一种类似于 XML 的 JS 扩展语法 JS XML 本质是 React.createElement(component, props, …children) 方法的语法糖&#xff0c;用来简化创建虚拟 DOM 写法&#xff1a;var ele <h1>Hello JSX!</h1&…

多个Python包懒得import,那就一包搞定!

使用Python时&#xff0c;有的代码需要依赖多个框架或库者来完成&#xff0c;代码开头需要import多次&#xff0c;比如&#xff0c; import pandas as pd from pyspark import SparkContext from openpyxl import load_workbook import matplotlib.pyplot as plt import seabo…

bootz启动 Linux内核涉及do_bootm_linux 函数

一. bootz启动Linux uboot 启动Linux内核使用bootz命令。当然还有其它的启动命令&#xff0c;例如&#xff0c;bootm命令等等。 本文只分析 bootz命令启动 Linux内核的过程中涉及的几个重要函数。具体分析 do_bootm_linux函数执行过程。 本文继上一篇文章&#xff0c;地址…

[netty-websocket-boot-starter] 基于Netty的轻量级、高性能的WebSocket框架

简介 这是一个轻量级、高性能的基于 Netty 的 WebSocket 框架&#xff0c;提升你的 WebSocket 开发体验&#xff0c;为 Spring Boot 带来全新的 WebSocket 功能。 该项目使你可以轻松集成 WebSocket 到你的 Spring Boot 项目中&#xff0c;提供了 Tomcat WebSocket 的简单性&a…

windows flask 多进程高并发

最近在做的一个项目&#xff0c;需要将十几个python函数封装程flask服务供外界调用&#xff0c;每个函数之间没有什么关系&#xff0c;相互独立。虽然感觉不是很难&#xff0c;但因为用的windows系统&#xff0c;遇到的坑比较多&#xff0c;在此一一总结一下。 flask偶尔出现卡…

多线程并发篇---第十二篇

系列文章目录 文章目录 系列文章目录一、说说ThreadLocal原理?二、线程池原理知道吗?以及核心参数三、线程池的拒绝策略有哪些?一、说说ThreadLocal原理? hreadLocal可以理解为线程本地变量,他会在每个线程都创建一个副本,那么在线程之间访问内部 副本变量就行了,做到了…

服务供应商安全管理制度

1、总则 1.1、目的 为了规范XXXXX单位信息系统建设和运行过程中服务商的选择&#xff0c;按照信息安全等级保护要求进行服务商管理&#xff0c;特制定本管理规范。 1.2、范围 本办法适用于XXXXX单位在信息化建设和运行过程中服务商选择的安全管理。 1.3、职责 较小项目由…

燃气管网监测系统,让城市生命线更安全

万宾科技燃气管网监测系统&#xff0c;让城市生命线更安全 城市是现代社会的中心&#xff0c;拥有庞大的人口和各种基础设施&#xff0c;以满足人们的生活需求。城市基础设施包括供热&#xff0c;供水&#xff0c;管廊&#xff0c;河湖&#xff0c;建筑&#xff0c;排水&#x…

JavaScript class和function的区别

待整理&#xff1a; 一 二 Class 组件和 Function 组件是 React 中创建组件的两种主要方式。他们在语法和功能上有一些不同。以下分点是 Class 组件和 Function 组件在不同方面的对比&#xff1a; 1. 语法结构 Class 组件&#xff1a; import React, { Component } from …

php代码优化---本人的例子

直接上货&#xff1a; 1&#xff1a;数据统计 店铺数量、提现金额、收益金额、用户数量 旧&#xff1a; // //店铺// $storey db( store )->whereTime( addtime, yesterday )->count();//昨天// $stored db( store )->whereTime( addtime, d )->count();//今天…

leetcode:210. 课程表 II

课程表 II 提示 中等 889 相关企业 现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在选修课程 ai 前 必须 先选修 bi 。 例如&#xff0c;想要学习课程…

【计算机网络】网络编程接口 Socket API 解读(10)

Socket 是网络协议栈暴露给编程人员的 API&#xff0c;相比复杂的计算机网络协议&#xff0c;API 对关键操作和配置数据进行了抽象&#xff0c;简化了程序编程。 本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍&#xff0c;从而更好的理解 socket 编程。…

linux 服务器类型Apache配置https访问

一&#xff1a;查看服务器类型&#xff0c;下载相应的SSL证书 命令&#xff1a;netstat -anp | grep :80 httpd是Apache超文本传输协议(HTTP)服务器的主程序&#xff0c;所以下载Apache证书 二&#xff1a;将证书解压后复制到服务器上 三个文件&#xff1a;xxx.key xxx_publ…

C //例6.8 输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。

C语言设计 &#xff08;第四版&#xff09; 谭浩强 例6.8 例6.8 输入一行字符&#xff0c;统计其中有多少个单词&#xff0c;单词之间用空格分隔开。 IDE工具&#xff1a;VS2010 Note: 使用不同的IDE工具可能有部分差异。 代码块 方法1&#xff1a;使用gets函数 #include …