当前位置: 首页 > 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; 有了多线…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…...