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

news2024/11/14 20:32:17

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

📃个人主页: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

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

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

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

相关文章

观测维度过大的一种ceres求解优化思路

详见 文章 这个优化如此重要&#xff0c;以至于需要单列一个文章。 使用场景&#xff1a; 比如lidar SLAM中优化点到面的距离&#xff0c;如果多个点关联到同一个面&#xff0c;那么就可以利用矩阵批量运算&#xff0c;假如有N个点&#xff0c;那么可以用一个factor来代替N个f…

【MYSQL】表操作

目录 查看当前数据库含有表查看表结构创建表插入&#xff08;新增create&#xff09;查询&#xff08;retrieve&#xff09;全列查询指定列查询查询列是表达式别名查询(as)去重查询(distinct)排序查询(order by)条件查询(where)比较/逻辑运算符使用 分页查询(limit) 一条语句各…

微服务-实现nacos的集群和Gateway网关的实现、认证校验、解决跨域

1. nacos的集群模式 1.1 分析 nacos在企业中的使用100%都是集群模式。需要掌握nacos集群的搭建 nacos的数据存放在derby本地磁盘中&#xff0c;nacos集群模式会导致数据库数据不一致&#xff0c;使用加一层思想&#xff0c;修改nacos的数据库&#xff0c;使用mysql数据库&…

kafka producer metrics

背景 做online Service埋点设计&#xff0c;塞了很多节点&#xff0c;采用了base64压缩&#xff0c;希望能监控当前消息的大小&#xff0c;防止超过threshold后无法正常发送。 kafka基本架构 producer metrics 官方文档 其中有两个参数用来表征在kafka的producer的client里&…

鸿蒙AI功能开发【hiai引擎框架-主体分割】 基础视觉服务

hiai引擎框架-主体分割 介绍 本示例展示了使用hiai引擎框架提供的主体分割能力。 本示例模拟了在应用里&#xff0c;选择一张图片&#xff0c;识别其图片中的显著性主体并展示出来主体的边界框的数据。 需要使用hiai引擎框架通用文字识别接口hms.ai.vision.subjectSegmenta…

加密软件有哪些常见的安全特性

1. 数据加密 核心功能&#xff1a;加密软件的核心在于对数据进行加密处理&#xff0c;通过复杂的加密算法&#xff08;如AES、RSA等&#xff09;将明文数据转换为密文&#xff0c;确保数据在存储和传输过程中的安全性。 透明加密&#xff1a;部分加密软件支持透明加密功能&am…

函数实例讲解(六)

文章目录 如何加密电话号码&#xff1f;&#xff08;SUBSTITUTE、REPLACE&#xff09;1、SUBSTITUTE2、REPLACE 用REPT函数做图表学生的考试成绩用A、B、C表示&#xff0c;如何找出哪个等级的学生最多呢&#xff1f;&#xff08;CHAR、CODE、MODE&#xff09;1、CHAR2、CODE3、…

【数据结构】顺序结构实现:特殊完全二叉树(堆)+堆排序

二叉树 一.二叉树的顺序结构二.堆的概念及结构三.堆的实现1.堆的结构2.堆的初始化、销毁、打印、判空3.堆中的值交换4.堆顶元素5.堆向上调整算法&#xff1a;实现小堆的插入6.堆向下调整算法&#xff1a;实现小堆的删除7.堆的创建1.堆向上调整算法&#xff1a;建堆建堆的时间复…

使用Go语言绘制折线统计图教程

使用Go语言绘制折线统计图教程 在本教程中&#xff0c;我们将学习如何使用Go语言及gg包绘制折线统计图&#xff0c;并将图表保存为PNG格式的图片。折线图适用于展示数据的变化趋势&#xff0c;并能直观地展示数据随时间或其他指标的变化。 安装gg包 首先&#xff0c;确保你已…

⌈ 传知代码 ⌋ 改进表情识别

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

Unity入门2——编辑器常用功能

点击文件夹&#xff0c;右键&#xff0c;选择showInExplorer可以在文件管理器中打开 选中多个图片素材&#xff0c;在Inspector里将Texture Type设置为Sprite(2D and UI)即可将图片素材用于UI 右键Hierachy面板的空白区&#xff0c;点击UI->Panel可以创建UI面板&#xf…

GPT-SoVITS-文本转语音(你的声音不再是唯一)

本文将要介绍GPT-SoVITS的安装和使用方法 首先感谢花儿不哭大佬带来的RVC声音克隆 花儿不哭&#xff1a; 花儿不哭的个人空间-花儿不哭个人主页-哔哩哔哩视频 (bilibili.com) GPT-SoVITS下载地址 GitHub - RVC-Boss/GPT-SoVITS: 1 min voice data can also be used to train a …

网络药理学:分子对接之pocasa/proteins.plus/Deepsite网站预测蛋白口袋

前言 配体的结合需要疏水作用&#xff0c;通常来说&#xff0c;疏水性空腔&#xff08;开口小、肚子大、能容纳一定体积的分子结构&#xff09;更有可能成为口袋。当我们复现网络药理学文章时可能经过前面的筛选&#xff0c;依旧有数个乃至数十个蛋白需要做分子对接验证。此时如…

算法——动态规划:基础

文章目录 一、基本介绍二、案例——斐波那契数列1. 基本介绍2. 递归实现3. 动态规划3.1 重叠子问题3.2 最优子结构3.3 无后效性3.4 性质的总结 4. 使用 动态规划 的思想实现4.1 自顶向下 的 递归4.2 自底向上 的 递推4.3 两种思路的简单比较 三、总结 一、基本介绍 动态规划&a…

python语言day4 函数 生成器yield、next关键字 装饰器

一、 函数 定义&#xff1a; def info(): print("执行info()函数") 直接调用方法和封装一个函数&#xff1a; 使用信息发送的功能为例 登录163网易免费邮-你的专业电子邮局 1&#xff09;开启POP3/SMTP服务&#xff0c;会得到对应的授权码&#xff0c;也就是登陆…

2024年旗舰骨传导耳机大对比:南卡、韶音和墨觉,哪款最值得购买?

作为专注于数码产品的博主&#xff0c;我对骨传导耳机的迅猛发展深有感触。这类耳机以其与众不同的技术和设计风格&#xff0c;成功捕捉了消费者的兴趣。它们独特的工作原理不仅保留了使用者对周围环境的感知&#xff0c;还能提供清晰的音乐播放和通话体验&#xff0c;特别适合…

第三方软件检测机构服务类型

在信息技术飞速发展的今天&#xff0c;软件产品的质量已成为企业竞争力的重要组成部分。卓码软件测评这家第三方软件检测机构致力于提供一流的软件测试服务&#xff0c;帮助企业确保其软件产品的可靠性和安全性。 一、项目验收测试&#xff1a;确保交付质量   项目验收测试是…

力扣-240.搜索二维矩阵(2)

刷力扣热题–第二十七天:240.搜索二维矩阵(2) 新手第二十七天 奋战敲代码&#xff0c;持之以恒&#xff0c;见证成长 1.题目简介 2.题目解答 这道题的想法就是,整体遍历,在遇到比target还大的,就停止这行的遍历,然后转过去继续遍历下一行,如果有一行的开头大于target,直接返回…

嘉盛平台的代理返佣机制:一份详细的说明书

在金融市场合作模式的多样性中&#xff0c;嘉盛平台的代理返佣机制无疑是一个引人注目的亮点。想了解更多关于嘉盛平台的代理返佣机制吗&#xff1f;本文将为您详细解答。嘉盛开户MT4平台开户链接 &#xff1a;https://application.jszhanghao.com/cn-meta/step/1?ibcodeFXAMM…

Windows--WSL2--Ubuntuon--Docker

编写目的&#xff1a; 在Windows上安装Docker&#xff0c;用Docker安装Gitlab、Jenkins等软件。 文章记录一下Windows上安装Docker的过程。 参考文档&#xff1a; 旧版 WSL 的手动安装步骤 | Microsoft Learn 下面用"参考文档"代替 目录 第一步&#xff1a;启…