【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

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

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

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

相关文章

Java代码生成器EasyCode

Java代码生成器EasyCode 一、安装插件二、连接数据库后右键Generator生成代码 一、安装插件 在 IntelliJ IDEA 的插件市场中搜索 EasyCode&#xff0c;然后安装该插件 二、连接数据库后右键Generator生成代码 勇敢面对挑战&#xff0c;成功从不会远离坚持者。坚持不懈的努力…

原生js: AI聊天功能, 仿照chatGPT问答功能

问: 现在我们需要一个ai聊天功能, 接口已经给出: 只要是message就是我们的数据, 是message_end就是结束信息, 其他的我们不需要管. 回答: 我们不使用传统的fetch请求这个接口, 而是使用sse, eventSource去请求, 当我们输入框回车 或者 点击元素, 获取到输入框中用户输入的值…

SpringSecurity+Mysql数据库实现用户安全登录认证

Spring Security 是一个提供身份认证、授权和防范常见攻击的安全权限框架。无论是对命令式&#xff0c;还是响应式web应用程序都完美支持&#xff0c;现在主要用作保护基于 Spring 框架的应用程序的事实标准。相对于shiro来说&#xff0c;SpringSecurity功能更加复杂而且更加强…

吴恩达:如何系统学习机器学习?

最近在知乎圆桌里看到吴恩达的回答&#xff0c;【如何系统学习机器学习&#xff1f;】颇为惊喜&#xff0c;仿佛看到了知乎刚成立时的样子&#xff0c;请各个行业大佬来分享专业知识。 该回答目前已经有三千多赞&#xff0c;评论区也相当火爆&#xff0c;一片膜拜之声。 吴恩…

Java线程池的这几个大坑,你踩过几个?

首先看一个简单的例子&#xff1a;代码可能会抛出空指针异常,但这个异常就会被吞掉。 要优雅解决问题&#xff0c;可以为线程池设置一个全局的异常处理器,使用自定义的线程工厂来设置! java public class CustomThreadFactory implements ThreadFactory { private final Threa…

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​函数实现计数器 …

LabVIEW工件表面瑕疵识别系统

开发了一种利用LabVIEW和IMAQ Vision视觉工具进行工件表面瑕疵识别的系统。该系统通过图像处理技术识别并分类工件表面的裂纹、划痕等缺陷&#xff0c;从而提升生产线的分拣效率和产品质量。 项目背景 工业生产中&#xff0c;工件表面的缺陷直接影响产品质量和生产效率。传统人…

Go语言加Vue3零基础入门全栈班11 Go语言+gorm用户管理系统实战 2024年08月03日 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…

Linux-入门-02

上节我们讲了如何安装虚拟机,本节课讲一些linux的常用命令,首先我们需要做一些配置,我们的centos的镜像是最小版安装,里面什么也没有,所以我们的linux是不能进行联网的,接下来我们就来一步一步联网 1、配置网络 首先我们需要先使用命令查看ip地址,linux中一切皆文件,只能使用命…

数据结构——排序(1):插入排序

目录 一、排序的概念 二、排列的运用 三、常见的排序算法 四、插入排序 1.直接插入排序 &#xff08;1&#xff09;思路 &#xff08;2&#xff09;过程图示 &#xff08;3&#xff09;代码实现 (4)代码解释 &#xff08;5&#xff09;特性 2.希尔排序 &#xff08;1…

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮

本例图片 案例分析 其实这个按钮非常的简单啊,主要就是利用了box-shadow的inset。 布局代码 <button class="base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-color:#99…

LLM - GQA 之 Group Query Attention 论文与源码精读

目录 Abstract 1.Introduction 2.Method 2.1 UpTraining 2.2 Grouped-query attention 3.Experiments 3.1 Experimental setup 3.2 Main Results 3.3 Ablations 4.Related Work 5.Conclustion 6.Code 6.1 repeat_kv 6.2 Attention Arg Init 6.3 Attention Mat I…

IT服务质量管理攻略(至简)

质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容&#xff0c;三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划&#xff0c;实施质量控制、质量保证和质量改进活动&#xff0c;确保IT服务满足服务级别协议的要求&#xff0c;最终获得用户…

openvidu私有化部署

openvidu私有化部署 简介 OpenVidu 是一个允许您实施实时应用程序的平台。您可以从头开始构建全新的 OpenVidu 应用程序&#xff0c;但将 OpenVidu 集成到您现有的应用程序中也非常容易。 OpenVidu 基于 WebRTC 技术&#xff0c;允许开发您可以想象的任何类型的用例&#xf…

[算法]第一集 递归(未完待续)

递归啊递归&#xff0c;说简单简单&#xff0c;说难难。 首先我们要知道 一、什么是递归&#xff1f; 我们再C语言和数据结构里都用了不少递归&#xff0c;这里就不多详细介绍。 递归简单来说就是函数自己调用自己的情况 二、为什么要用递归呢&#xff1f; 本质来说其实就…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;32选择#2&#xff1a;33选择#3&#xff1a;34 1.5 检查WRF是否…

Linux 中的信号处理

Linux 中的信号处理是操作系统中非常重要的一个概念&#xff0c;通过信号处理&#xff0c;进程之间可以进行通信、协调以及实现一些重要的功能。本文将从信号的概念、类型、生成、传递、处理、以及常见的信号处理函数等方面展开讨论&#xff0c;以帮助读者更深入地了解 Linux 中…

【机器学习】BP神经网络中的链式法则

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 BP神经网络中的链式法则1. 引言2. 链式法则基础2.1 什么是链式法则&#xff1f;…

29.Labview界面设计(下篇) --- 自定义控件库、界面布局与外观设计

摘要&#xff1a; 题主在上一篇文章中向大家讲解了前面板逻辑框架及结构的搭建和控件的类型介绍&#xff0c;那么本章主要围绕前面板的控件布局以及控件的自定义类型和背景等外观优化项中来讲解。 本篇文章讲解界面设计的下篇内容&#xff0c;上篇内容链接大家可以直接点击链接…

国家统计局中国主要城市面板数据(1990-2023年)

数据说明&#xff1a;数据来源于国家统计局&#xff0c;指标包含&#xff1a;城市、年份、第三产业增加值、第一产业增加值 地区生产总值、第二产业增加值、年末户籍人口、城镇非私营单位在岗职工平均工资 房地产开发投资额、房地产开发住宅投资额、房地产开发办公楼投资额、房…