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

【C++高阶】:自定义删除器的全面探索

✨                                          我凌于山壑万里,一生自由随风起    🌏

📃个人主页:island1314

🔥个人专栏:C++学习

🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


目录

🚀前言

1. 删除器的基本概念

1.1 默认删除器

1.2 自定义删除器(定制删除器)

1.3 为什么需要自定义删除器

1.2.1 管理非堆内存资源

1.2.2 代码可读性和维护性

1.2.3 异常安全

1.4 自定义删除器的使用示例

2、自定义删除器的设计

2.1 函数对象(Functor)作为删除器

2.1.1 什么是函数对象

2.1.2 如何使用函数对象作为自定义删除器

2.2 Lambda表达式作为删除器

2.2.1 Lambda表达式的基础

2.2.2 如何使用Lambda表达式作为自定义删除器

4.3 与std::function结合

3 自定义删除器的需求场景

1.3.1 非堆内存资源

1.3.2 第三方库


🚀前言

这篇文章主要是对之前智能指针的一个小小的补充,没有看过智能指针的读者朋友们,可以参考下下面这篇博客

【C++高阶】:智能指针的全面解析-CSDN博客

1. 删除器的基本概念

在C++中,智能指针(Smart Pointers)如std::unique_ptr和std::shared_ptr默认使用delete或delete[]来释放内存。但有时,这种默认行为可能不适用于所有场景。这就是自定义删除器(Custom Deleters)进入游戏的地方。

比如:std::share_ptr 在实例化对象时,有两个参数:

template <class U, class D>
shared_ptr (U* p, D del);

其中:

  • p:需要让智能指针管理的资源。
  • del:删除器,这个删除器是一个可调用对象,比如函数指针、仿函数、lambda表达式以及被包装器包装后的可调用对象。

实际上,删除器就是一个被工具封装的动作,这个动作就是用特定的方式释放资源。

总的来说,当智能指针管理的资源不是通过new出来的时候,就需要用对象类型和定制删除器构造智能指针。

1.1 默认删除器

默认情况下,std::unique_ptr和std::shared_ptr使用以下方式进行删除:

delete ptr;
delete[] arr_ptr;

这些删除器在大多数情况下都很有用,但有时我们需要更多的灵活性。

1.2 自定义删除器(定制删除器)

实际上,不是所有的对象都是new出来的,也可能是new[],因此释放对象的资源也可能是delete[]。例如:

  • 当你想在释放对象时执行一些额外的操作,例如关闭文件、释放资源、记录日志等。
  • 当你想使用一个不同于delete的函数来销毁对象,例如free、fclose、Release等。
  • 当你想管理一个不是通过new分配的对象,例如一个栈上的对象或一个全局变量。
  • 当你想管理一个不是单个对象而是一个数组或容器的对象。
  • 定制删除器可以让你更灵活地控制shared_ptr如何管理和释放它所指向的对象。

定制删除器可以让你更灵活地控制shared_ptr如何管理和释放它所指向的对象。

1.3 为什么需要自定义删除器

1.2.1 管理非堆内存资源

除了内存,智能指针还可以用于管理其他类型的资源,例如文件句柄、互斥锁或数据库连接。这些资源可能需要特定的释放机制。

1.2.2 代码可读性和维护性

使用自定义删除器可以提高代码的可读性和维护性。它使资源的获取和释放逻辑紧密地绑定在一起,从而减少了出错的机会。

1.2.3 异常安全

自定义删除器有助于实现异常安全(Exception Safety)。当构造函数可能抛出异常时,使用智能指针和自定义删除器可以确保资源被正确释放。

毕竟即使是最简单的代码也可能隐藏复杂性和潜在的错误。而自定义删除器提供了一种机制,可以在复杂的错误处理逻辑中保持清晰和简洁。

1.4 自定义删除器的使用示例

我们要想管理一个打开的文件,但是你不能使用delete来关闭它,而是使用fclose:

#include <iostream>
#include <memory>
#include <cstdio>int main()
{// 创建一个shared_ptr,管理一个打开的文件// 使用fclose作为定制删除器std::shared_ptr<FILE> file(fopen("test.txt", "w"), fclose);// 写入一些内容到文件fputs("Hello world", test.get());// 当test离开作用域时,会调用fclose来关闭文件
}

这样就可以避免使用delete来释放一个不是通过new分配的对象,从而导致危险行为。

或者我们可以自己写一个Fclose的模板来使用,如下:

class Fclose {
public:void operator()(FILE* ptr) {cout << "fclose:" << ptr << endl;fclose(ptr);}
};
int main()
{//使用自定义的fclose模板std::unique_ptr<FILE, Fclose> up(fopen("test.txt", "r"));std::shared_ptr<FILE> sp(fopen("test.txt", "r"), Fclose());
}

方法适用场景优点缺点
默认删除器堆内存简单、高效不够灵活
函数对象(Functor)需要状态的复杂资源管理灵活、可维护可能增加内存开销
Lambda表达式简单的自定义逻辑简洁、现代不能携带状态
    std::function需要多态删除器高度灵活性能和内存开销

2、自定义删除器的设计

2.1 函数对象(Functor)作为删除器

🎈在C++中,函数对象(Functor)是一种非常灵活的机制,它允许我们将行为(behavior)封装为对象。这在设计自定义删除器时非常有用。

2.1.1 什么是函数对象

🎈函数对象是重载了operator()的类或结构体。这意味着你可以像调用函数一样使用这些对象。

struct MyDeleter {void operator()(int* ptr) {delete ptr;}
};
2.1.2 如何使用函数对象作为自定义删除器

🎈使用std::unique_ptr(唯一指针)或std::shared_ptr(共享指针)时,你可以将函数对象作为第二个模板参数传递。

std::unique_ptr<int, MyDeleter> p(new int, MyDeleter());

这种方式的优点是类型安全和高效。因为删除器是类型的一部分,编译器可以在编译时进行优化。

2.2 Lambda表达式作为删除器

🎈Lambda表达式(Lambda Expression)在C++11后成为了语言的一部分,它提供了一种更简洁、更直观的方式来定义简单的函数对象

2.2.1 Lambda表达式的基础

🎈Lambda表达式基本上是一个匿名函数。你可以这样使用它:

auto deleter = [](int* ptr) { delete ptr; };
2.2.2 如何使用Lambda表达式作为自定义删除器

🎈与函数对象类似,Lambda表达式可以直接作为std::unique_ptr或std::shared_ptr的删除器。

std::unique_ptr<int, decltype(deleter)> p(new int, deleter);

这种方式的优点是简洁和直观。你不需要定义一个完整的结构体或类,只需要一个简单的Lambda表达式。

4.3 与std::function结合
 

3 自定义删除器的需求场景

🌈虽然标准库提供的智能指针非常强大,但有时候它们还是不能满足所有需求。例如,当你需要管理的不仅仅是内存,可能是一个文件句柄(File Handle)或者数据库连接(Database Connection)时,标准的删除器就显得力不从心。

比如:你正在与一个老旧的C库交互,该库要求使用特定的函数来释放内存,例如。在这种情况下,使用默认的delete将不适用。

1.3.1 非堆内存资源

🌈在许多情况下,你可能需要管理的资源并不是通过 newdelete 分配的堆内存。这些资源可能是操作系统级别的,比如文件句柄或线程。这时,你需要一个更加灵活的删除器。

1.3.2 第三方库

🌈当你的代码需要与第三方库集成时,这些库可能有自己的资源管理机制。在这种情况下,使用自定义删除器可以让你的智能指针与第三方库的资源管理无缝对接。

“We cannot solve our problems with the same thinking we used when we created them.”

- Albert Einstein

这句话在这里意味着,当面对新的问题时,我们需要新的解决方案。自定义删除器就是这样一种解决方案,它让智能指针更加灵活,能适应更多的场景。

相关文章:

【C++高阶】:自定义删除器的全面探索

✨ 我凌于山壑万里&#xff0c;一生自由随风起 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&am…...

Java中的不可变集合、Stream流以及异常处理的

目录 1. 不可变集合 如何创建不可变集合 2. Stream流 Stream基本操作 3. 异常处理 异常的分类 异常处理机制 1. 不可变集合 在Java中&#xff0c;不可变集合指的是一旦创建后内容不可更改的集合。这种集合的好处在于它们可以安全地被多个线程访问而无需同步&#xff0c;…...

LeetCode面试题Day1|LeetCode26 删除有序数组中的重复项、LeetCode80 删除有序数组中的重复项Ⅱ

前言&#xff1a; 暑假实在不知道干什么了&#xff0c;做一下力扣的《面试经典150题》吧&#xff0c;记录一下学习轨迹。(如果有要打非中文竞赛或者精进一下英语水平的记得把力扣调成英文) 题目1&#xff1a; 指路&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;26…...

细说文件操作

你好&#xff01;感谢支持孔乙己的新作&#xff0c;本文就文件操作与大家分享我的思路。 希望能大佬们多多纠正及支持 &#xff01;&#xff01;&#xff01; 个人主页&#xff1a;爱摸鱼的孔乙己-CSDN博客 目录 1.什么是文件 1.1.程序设计文件 1.1.1.程序文件 1.1.2.数据文…...

Vue3从零开始——掌握setup、ref和reactive函数的奥秘

文章目录 一、Vue 3 组合式 API 概述二、setup​ 函数的基本使用2.1 setup​ 函数的特点2.2 setup​ 函数的基本结构2.3 实现一个简单的小demo 三、ref​ 函数的功能和应用3.1 ref​函数介绍3.2 基本使用3.2.1 定义ref​数据3.2.2 修改响应式变量 3.3 使用ref​函数实现计数器 …...

C语言练习--屏幕上打印九九乘法表

int main() { int i 0; for (i 1; i < 10; i) { int j 0; for (j 1; j <i; j) { printf(" %d*%d%2d", i, j, i * j); } printf("\n"); } return 0; }...

将tsx引入vue

按钮 vue <cl-batch-btn >新增批量</cl-batch-btn> import batch from "//modules/ad/components/ uploading/batch.vue" import ClBatchBtn from "/~/crud/src/components/batch-btn"; tsx...

前端实现签字效果+合同展示

文章目录 获取一个高度会变的元素的高度获取元素设置的 transform适配手机transform-origin: 5% 0; 的原因修改后 签字效果取消el-dialog的头部边距为什么禁止界面滚动vue3 使用 nextTick实现效果 签字判断是横是竖canvas 去掉空白部分canvas裁剪图片最终完善代码&#xff0c;可…...

[AI Embedchain] 开始使用 - 快速开始

安装 首先安装 Python 包&#xff1a; pip install embedchain安装包后&#xff0c;根据您的偏好&#xff0c;您可以选择使用以下内容&#xff1a; 开源模型 本节提供了一个快速入门示例&#xff0c;展示了如何使用 Mistral 作为开源 LLM&#xff08;大型语言模型&#xff…...

Linux网络协议.之 tcp,udp,socket网络编程(三).之多进程实现并发demon

一、fork创建进程&#xff0c;来实现多并发 这只是个demon&#xff0c;并不能用于实际项目&#xff0c;多进程&#xff0c;消耗太多资源。没有人这么玩 1、服务端代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #in…...

Java线程(练习题)

Exercise 创建三个线程&#xff1a;一个线程打印 100个A,一个线程打印 100 个 B &#xff0c;一个线程打印 100个C 输出效果&#xff1a;ABC ABC ABC…交替打印 package com.kane.exercise01;public class PrintABC implements Runnable {private static final Object lock …...

MySQL:初识数据库初识SQL建库

目录 1、初识数据库 1.1 什么是数据库 1.2 什么是MySQL 2、数据库 2.1 数据库服务&数据库 2.2 C/S架构 3、 初识SQL 3.1 什么是SQL 3.2 SQL分类 4、使用SQL 4.1 查看所有数据库 4.1.2 语句解析 4.2 创建数据库 4.2.1 if not exists校验 4.2.2 手动明确字符集…...

关于Redis的集群面试题

问题一&#xff1a;Redis的多数据库机制&#xff0c;了解多少&#xff1f; Redis支持多个数据库&#xff0c;并且每个数据库是隔离的不能共享&#xff0c;单机下的redis可以支持16个数据库&#xff08;db0~db15&#xff09;;若在Redis Cluster集群架构下&#xff0c;则只有一个…...

带头双向循环链表(一)

今天我们来学习带头双向链表 带头双向循环链表的解释 带头双向链表顾名思义就是&#xff1a; 1、带了一个“头”在数据结构中的意思就是加了一个"哨兵位"。 2、这个链表是双向循环的链表即可以通过任意的节点访问它的上一个和下一个的节点也能通过链表的头直接访…...

深入理解Win32K.sys的工作原理

https://download.csdn.net/download/sitelist/89621815 Windows Resource Kits 2003.rar工具下载&#xff0c;因为有windows server 2003源代码&#xff0c;并可以编译成iso&#xff0c;所以对于研究windows系统很有帮助&#xff0c;上吗是2003的研究工具&#xff0c;不知道源…...

力扣面试经典算法150题:删除有序数组中的重复项

删除有序数组中的重复项 今天的题目是力扣面试经典150题中的数组的简单题: 删除有序数组中的重复项 题目链接&#xff1a;https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/?envTypestudy-plan-v2&envIdtop-interview-150 题目描述 给定一…...

文本加密工具类-支持MD5、SHA1、SHA256、SHA224、SHA512、SHA384、SHA3、RIPMD160算法

文本加密工具类 1.算法简介1.1 MD51.2 SHA-11.3 SHA-2&#xff08;推荐使用&#xff09;1.4 SHA-3&#xff08;推荐使用&#xff09;1.5 RIPEMD-160 2.工具类案例2.1POM导入2.2代码编写2.3 输出示例 1.算法简介 1.1 MD5 MD5 (Message-Digest Algorithm 5) 描述&#xff1a;M…...

LVS集群中的负载均衡技术

目录 一、LVS技术原理 二、NAT模式原理及部署方法 1、工作原理 2、部署方法 1、网络配置 2、软件安装与启用 3、测试 三、DR模式原理及部署方法 1、工作原理 2、部署方法 1、网络配置 2、解决vip响应问题 3、测试 四、ipvsadm命令及参数 1、管理集群服务&#x…...

Java网络编程——HTTP协议原理

协议 我们在网上冲浪时&#xff0c;会在浏览器地址栏输入一个网址&#xff0c;然后就能打开网页了。比如&#xff0c;输入 https://www.douban.com/就可以访问到豆瓣的主页&#xff1a; 那么大家是否好奇&#xff1a;https 是什么意思&#xff0c;作用又是什么呢&#xff1f;…...

java之多线程篇

一、基本概念 1.什么是线程&#xff1f; 线程就是&#xff0c;操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。简单理解就是&#xff1a;应用软件中互相独立&#xff0c;可以同时运行的功能 2.什么是多线程&#xff1f; 有了多线…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...