Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
C++智能指针自定义删除器 资源清理回调_创想鸟

C++智能指针自定义删除器 资源清理回调

自定义删除器使智能指针能管理文件句柄、网络连接等非内存资源,通过RAII确保资源安全释放,提升代码健壮性与通用性。

c++智能指针自定义删除器 资源清理回调

C++智能指针的自定义删除器,本质上就是为智能指针提供一个“如何释放”的特殊指令,让它在管理内存之外,还能妥善处理文件句柄、网络连接或其他需要特定清理流程的资源。这使得智能指针的应用范围大大扩展,从单纯的内存管理工具升级为通用的资源管理利器,让资源管理变得更加自动化、安全且富有弹性。

解决方案

在C++中,智能指针(主要是

std::unique_ptr

std::shared_ptr

)默认知道如何使用

delete

操作符来释放它们所管理的堆内存。但现实世界中,我们常常需要管理那些并非通过

new

分配,或者需要特定函数(如

fclose

CloseHandle

free

等)来释放的资源。这时候,自定义删除器就派上用场了。它允许我们告诉智能指针,当它不再需要管理某个资源时,应该调用哪个函数或执行哪段代码来完成清理工作。

我们通常有几种方式来定义这个“清理指令”:函数对象(Functor)、Lambda表达式或者普通的函数指针。

1. 使用

std::unique_ptr

自定义删除器

立即学习“C++免费学习笔记(深入)”;

std::unique_ptr

的自定义删除器类型是其模板参数的一部分,这意味着如果你使用了一个带有自定义删除器的

unique_ptr

,它的完整类型会包含这个删除器类型。

函数对象(Functor)作为删除器:当清理逻辑比较复杂,或者希望删除器能携带一些状态时,函数对象是个不错的选择。

#include #include #include  // For FILE*// 定义一个文件关闭器,它是一个函数对象struct FileCloser {    void operator()(FILE* fp) const {        if (fp) {            std::cout << "DEBUG: Closing file handle via FileCloser." << std::endl;            fclose(fp);        }    }};void demo_unique_ptr_functor() {    // unique_ptr的第二个模板参数需要指定删除器的类型    std::unique_ptr logFile(fopen("app.log", "w"));    if (logFile) {        fprintf(logFile.get(), "Application started.n");        // ... 更多操作 ...    }    std::cout << "INFO: logFile unique_ptr scope ending." << std::endl;    // 当logFile超出作用域时,FileCloser::operator()会被调用}

Lambda表达式作为删除器:对于一次性、局部性的清理任务,Lambda表达式非常简洁高效,尤其当它需要捕获一些上下文变量时。

#include #include #include  // For std::jthread example#include  // For std::move// 假设我们有一个需要join的线程void demo_unique_ptr_lambda_thread() {    auto thread_deleter = [](std::jthread* t) {        if (t && t->joinable()) {            std::cout << "DEBUG: Joining thread via lambda deleter." <join();            delete t; // 别忘了释放线程对象本身        } else if (t) {            delete t;        }    };    // unique_ptr的第二个模板参数需要通过decltype获取lambda的类型    std::unique_ptr        worker(new std::jthread([]{            std::cout << "Worker thread running..." << std::endl;            std::this_thread::sleep_for(std::chrono::milliseconds(100));            std::cout << "Worker thread finished." << std::endl;        }), thread_deleter);    std::cout << "INFO: Main thread waiting for worker." << std::endl;    // worker超出作用域时,lambda会被调用,确保线程被join}

函数指针作为删除器:最简单直接的方式,适用于无状态的全局或静态清理函数。

#include #include #include  // For free// 一个通用的free函数,适用于通过malloc分配的内存void free_deleter(void* ptr) {    if (ptr) {        std::cout << "DEBUG: Calling free() via function pointer deleter." << std::endl;        free(ptr);    }}void demo_unique_ptr_func_ptr() {    // unique_ptr的第二个模板参数是函数指针类型    std::unique_ptr data(        static_cast(malloc(sizeof(int))), &free_deleter);    if (data) {        *data = 42;        std::cout << "Data allocated with malloc: " << *data << std::endl;    }    std::cout << "INFO: data unique_ptr scope ending." << std::endl;    // data超出作用域时,free_deleter会被调用}

2. 使用

std::shared_ptr

自定义删除器

std::shared_ptr

处理自定义删除器的方式与

unique_ptr

略有不同。它的删除器类型不是其自身模板参数的一部分,而是作为构造函数的一个参数传递。这意味着所有

shared_ptr

实例都可以有不同的删除器,而它们的类型仍然是

std::shared_ptr

。这得益于

shared_ptr

内部的类型擦除机制。

#include #include #include #include  // For std::ofstream// 假设我们有一个自定义的资源类,需要特定的关闭方法class NetworkConnection {    std::string _host;    int _port;    bool _isOpen = false;public:    NetworkConnection(const std::string& host, int port) : _host(host), _port(port) {        std::cout << "NetworkConnection to " << _host << ":" << _port << " established." << std::endl;        _isOpen = true;    }    void send(const std::string& data) {        if (_isOpen) {            std::cout << "Sending data to " << _host << ": " << data << std::endl;        }    }    void close() {        if (_isOpen) {            std::cout << "NetworkConnection to " << _host << ":" << _port << " closed." << std::endl;            _isOpen = false;        }    }    // 析构函数,但我们希望通过自定义删除器来调用close()    ~NetworkConnection() {        std::cout << "NetworkConnection destructor called (should be after custom close)." << std::endl;    }};void demo_shared_ptr_custom_resource() {    // shared_ptr在构造时直接传入删除器,类型是std::shared_ptr    std::shared_ptr conn(        new NetworkConnection("example.com", 8080),        [](NetworkConnection* nc) {            if (nc) {                std::cout << "DEBUG: Custom deleter for NetworkConnection called." <close(); // 调用资源的特定关闭方法                delete nc;   // 然后释放内存            }        }    );    if (conn) {        conn->send("Hello, server!");    }    std::cout << "INFO: conn shared_ptr scope ending." << std::endl;    // 当conn的引用计数归零时,lambda删除器会被调用}// 另一个shared_ptr例子:管理std::ofstreamvoid demo_shared_ptr_ofstream() {    std::shared_ptr outFile(        new std::ofstream("output.txt"),        [](std::ofstream* os) {            if (os && os->is_open()) {                std::cout << "DEBUG: Custom deleter closing std::ofstream." <close();                delete os;            } else if (os) {                delete os;            }        }    );    if (outFile && outFile->is_open()) {        *outFile << "Writing something to output.txtn";    }    std::cout << "INFO: outFile shared_ptr scope ending." << std::endl;}int main() {    demo_unique_ptr_functor();    std::cout << "------------------------n";    demo_unique_ptr_lambda_thread();    std::cout << "------------------------n";    demo_unique_ptr_func_ptr();    std::cout << "------------------------n";    demo_shared_ptr_custom_resource();    std::cout << "------------------------n";    demo_shared_ptr_ofstream();    return 0;}

可以看到,自定义删除器极大地扩展了智能指针的能力,让它们能够成为管理各种类型资源的通用工具,而不仅仅是内存。

C++智能指针自定义删除器的核心价值:超越内存管理的资源封装

说实话,刚接触智能指针时,很多人(包括我)可能只把它当成

new

/

delete

的自动版本。但随着项目深入,你会发现很多资源并非简单地用

delete

就能搞定。比如文件句柄需要

fclose

,网络套接字需要

closesocket

,互斥锁需要

unlock

,甚至一些C库分配的内存需要

free

而不是

delete

。这些非内存资源的清理逻辑,如果每次都靠手动调用,那简直是噩梦。一个不小心,资源泄漏就发生了,调试起来非常痛苦。

自定义删除器就是为了解决这些痛点而生的。它把资源获取(Resource Acquisition)和资源释放(Resource Release)紧密地绑定在一起,完美地体现了RAII(Resource Acquisition Is Initialization)原则。当我拿到一个文件句柄,我立刻把它包装进一个带有

fclose

删除器的

unique_ptr

里,那么无论我的代码在中间出了什么岔子,抛了异常也好,提前返回也罢,只要这个

unique_ptr

离开作用域,文件句柄就一定会被妥善关闭。这不仅大大提升了代码的健壮性和异常安全性,也让资源管理逻辑变得更加清晰和集中。从某种意义上说,它将智能指针从一个“内存管家”升级成了“全能资源管理员”。

C++智能指针自定义删除器选择指南:函数对象、Lambda与函数指针的优劣

在选择自定义删除器的实现方式时,我通常会根据实际需求来权衡。这三种方式各有千秋,没有绝对的“最佳”,只有“最适合”。

函数对象(Functor):这是最灵活的一种方式,因为它本质上是一个类。你可以让它携带状态,比如在删除器中记录一些日志信息,或者根据内部状态决定不同的清理策略。如果你的清理逻辑比较复杂,或者希望这个删除器可以在多个地方复用,并且需要一些配置参数,那么函数对象是首选。它的缺点是需要额外定义一个

struct

class

,对于简单的清理任务来说,可能会显得有点“杀鸡用牛刀”。另外,对于

std::unique_ptr

,函数对象的类型会成为

unique_ptr

类型的一部分,这在模板元编程或类型推导时可能需要额外注意。

Lambda 表达式:这是我个人最常用的一种方式,尤其是在C++11及更高版本中。Lambda表达式的优势在于简洁和局部性。你可以在需要的地方直接定义清理逻辑,并且可以轻松捕获当前作用域的变量,这使得它非常适合处理那些与特定上下文相关的资源清理。例如,一个删除器可能需要访问某个日志对象来记录关闭信息,或者需要一个ID来标识正在关闭的资源。Lambda的缺点嘛,对于

std::unique_ptr

,你可能需要使用

decltype

来获取lambda的类型,这让代码看起来略微复杂一点点。但对于

std::shared_ptr

,它简直是完美搭配,因为

shared_ptr

的删除器类型是擦除的。

函数指针:这是最传统、最简单的方式,适用于那些无状态、通用的清理函数。比如,如果你需要用

free()

来释放

malloc()

分配的内存,那么直接传入

&free

作为删除器就非常直观。它的优点是类型简单,开销小。但缺点也很明显,它不能捕获任何状态,所以如果你的清理逻辑依赖于某些运行时数据,函数指针就无能为力了。通常,我只会在清理操作非常标准且不依赖任何上下文时才考虑它。

总结一下,如果清理逻辑简单且无状态,函数指针或简单lambda就够了。如果需要捕获上下文或处理复杂状态,lambda通常是我的首选。而如果删除器本身需要高度复用、配置或者承载复杂逻辑,那么函数对象无疑是更强大的工具。

C++智能指针自定义删除器:避免常见错误与提升代码健壮性的实践

自定义删除器虽然强大,但用不好也容易踩坑。我在实际开发中就遇到过一些问题,总结下来,有些陷阱是需要特别留意的,同时也有一些实践能让代码更健壮。

常见陷阱:

删除器不处理

nullptr

这是一个很常见的疏忽。智能指针在析构时,可能其内部管理的指针已经是

nullptr

(例如,它可能被

release()

了,或者在构造时就传入了

nullptr

)。你的自定义删除器必须能安全地处理

nullptr

,否则可能导致

以上就是C++智能指针自定义删除器 资源清理回调的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1473924.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
C++中二级指针(指向指针的指针)在什么场景下会用到
上一篇 2025年12月18日 20:59:46
C++初学者如何使用ifstream逐行读取文本文件的内容
下一篇 2025年12月18日 20:59:59

相关推荐

  • 苹果手机官网查询门户 苹果查询官网手机入口

    苹果查询官网手机入口是https://www.apple.com.cn,用户可通过该网站查询设备信息、技术支持及配件服务,包括序列号验证、保修状态、维修预约和原厂配件真伪核验等功能。 苹果手机官网查询门户在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来苹果查询官网手机入口,感兴趣的网友一起…

    2026年9月23日
    000
  • 支持多种CPU调整外频!微星B850MPOWER主板图赏

    支持多种CPU调整外频!微星B850MPOWER主板图赏支持多种CPU调整外频!微星B850MPOWER主板图赏支持多种CPU调整外频!微星B850MPOWER主板图赏支持多种CPU调整外频!微星B850MPOWER主板图赏

    10月9日最新消息,微星b850m power主板已正式上市,定价为1599元,首发时迅速售罄,目前仍处于供不应求状态,需参与抢购才能入手。 这款热门新品现已抵达我们评测室,接下来为大家带来详细的图赏内容。 微星B850M POWER主板采用高端的8层服务器级PCB设计,配备双8PIN供电接口,并使…

    2026年9月23日 用户投稿
    000
  • Deepseek 满血版联动 Typinator Pro,创建复杂文本模板​

    Deepseek 满血版联动 Typinator Pro,创建复杂文本模板​Deepseek 满血版联动 Typinator Pro,创建复杂文本模板​Deepseek 满血版联动 Typinator Pro,创建复杂文本模板​Deepseek 满血版联动 Typinator Pro,创建复杂文本模板​

    将 ai 与 typinator 联动可打造高效文本模板系统。1. 使用 deepseek 等 ai 工具生成结构化内容,如邮件草稿;2. 将生成内容调整为 typinator 变量格式(如 %|name%);3. 导入 typinator 并设置快捷短语,实现一键插入。典型场景包括批量写邮件、报告…

    2026年9月23日 用户投稿
    000
  • Vscode怎么批量修改变量?Vscode变量重命名操作指南

    Vscode怎么批量修改变量?Vscode变量重命名操作指南Vscode怎么批量修改变量?Vscode变量重命名操作指南Vscode怎么批量修改变量?Vscode变量重命名操作指南Vscode怎么批量修改变量?Vscode变量重命名操作指南

    vscode批量修改变量的方法是选中变量按f2输入新名回车,但要更精确控制重命名范围可采取以下措施:1.使用“查找和替换”功能(ctrl+h),结合正则表达式限定匹配范围;2.使用语言支持的“重构”功能(右键→重构→重命名),基于语义分析避免误改;3.安装增强重命名插件提升功能;4.利用静态代码分析…

    2026年9月23日 用户投稿
    000
  • Java中用户输入验证:正确使用equals()或转换为整数进行比较

    本教程详细阐述了Java中用户输入字符串(如菜单选项)验证的正确方法。针对==运算符在字符串比较中的局限性,文章介绍了两种解决方案:一是使用String.equals()方法进行内容比较,二是将字符串输入解析为整数后进行数值比较。通过代码示例,帮助开发者避免常见的字符串比较错误,确保程序逻辑的健壮性…

    2026年9月23日
    000
  • 微软免费文件恢复工具介绍

    微软免费文件恢复工具介绍微软免费文件恢复工具介绍微软免费文件恢复工具介绍微软免费文件恢复工具介绍

    现在好了,最近微软在 windows 10 应用商店中上架了一款免费的数据删除恢复工具 windows file recovery,猿妹再也不用担心误删文件了。 Windows File Recovery基于命令行程序设计,大小仅有8.26MB,所有Windows 10用户均可免费下载使用。 无论是…

    2026年9月23日 用户投稿
    000
  • VSCode精简配置Perl:语法检查、中文编码、正则调试

    vscode中perl语法检查不生效的主要原因是perl解释器路径未正确配置或缺失,解决方法是在settings.json中明确设置”perl.perlpath”指向正确的perl可执行文件;其次是因缺少cpan模块导致检查失败,需安装对应模块;此外,多个perl扩展冲突、大…

    2026年9月23日
    000
  • Yandex对俄贸易专用搜索 官方网站入口一键直达

    Yandex对俄贸易专用搜索官网为https://yandex.com/,提供俄语内容索引、企业信息查找、地图定位及自动翻译等功能,支持跨境业务拓展与市场分析。 Yandex对俄贸易专用搜索官方网站入口在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来Yandex对俄贸易专用搜索官方网站入口…

    2026年9月23日
    300
  • AfterEffects如何制作AI动效视频?创建动态AI视频的完整教程

    AfterEffects如何制作AI动效视频?创建动态AI视频的完整教程AfterEffects如何制作AI动效视频?创建动态AI视频的完整教程AfterEffects如何制作AI动效视频?创建动态AI视频的完整教程AfterEffects如何制作AI动效视频?创建动态AI视频的完整教程

    答案是掌握AE动画原理并融合AI素材进行创作。需根据风格选择合适AI工具(如Midjourney、RunwayML),注重素材质量、可定制性与透明背景支持;将AI素材导入AE后,调整分辨率,运用颜色校正、模糊、跟踪及表达式增强效果;通过关键帧控制位置、缩放、旋转,并应用缓动实现流畅动画;搭配契合主题…

    2026年9月23日 用户投稿
    200
  • 如何在Java中安装Eclipse开发环境

    先安装JDK并配置环境变量,再下载安装Eclipse IDE。1. 安装JDK:从Oracle或Eclipse Adoptium下载JDK 17/21,按提示安装,设置JAVA_HOME和PATH,用java -version验证。2. 安装Eclipse:官网下载“Eclipse IDE for …

    2026年9月23日
    000
  • 配置Linux下vim自动缩进

    从终端打开配置文件: vim ~/.vimrc 添加如下代码: set tabstop=4set softtabstop=4set shiftwidth=4set autoindentset cindentset cinoptions={0,1s,t0,n-2,p2s,(03s,=.5s,>1…

    2026年9月23日
    800
  • 2025 旗舰手机选购指南:四款机型精准匹配你的需求

    2025 旗舰手机选购指南:四款机型精准匹配你的需求2025 旗舰手机选购指南:四款机型精准匹配你的需求2025 旗舰手机选购指南:四款机型精准匹配你的需求2025 旗舰手机选购指南:四款机型精准匹配你的需求

    在智能手机市场持续繁荣的今天,面对琳琅满目的旗舰%ignore_a_1%,用户往往难以抉择。本文精选四款当前热门的高端手机,帮你高效锁定心仪之选。 1 五款旗舰机型推荐 1. OPPO Find X9 系列 大容量电池、护眼显示屏、双 2 亿像素影像系统——适合对续航敏感、热爱摄影以及重视视觉健康的…

    2026年9月23日 用户投稿
    200
  • PHP多维数组重构:按指定键分组数据

    本文旨在指导读者如何将一个包含多个关联数组的扁平数组,根据其中某个特定键(如object_type)的值,重构为一个多维数组。通过遍历原始数据并动态构建新结构,最终实现数据按指定键值进行高效分组,以便于后续的数据处理和管理。 1. 引言与问题背景 在PHP开发中,我们经常会遇到需要处理和转换数组结构…

    2026年9月23日
    000
  • VSCode如何实现AI辅助编程 VSCode Copilot插件的深度使用指南

    github copilot能显著提升编程效率,但需合理使用。1. 安装插件并登录github账号是基础步骤;2. 提供清晰的上下文,如规范命名和详细注释,可提高生成代码的准确性;3. 利用快捷键切换多个建议,筛选最优方案并进行修改;4. 对生成代码必须严格审查,尤其关注安全性与业务逻辑匹配度;5.…

    2026年9月23日
    100
  • 抖音ai分身怎么弄出来?抖音分身在哪里打开

    随着人工智能技术不断发展,其应用已经深入到我们日常生活的诸多领域。作为当前热门的短视频平台之一,抖音也推出了AI分身功能,让用户可以轻松创建属于自己的虚拟形象。本文将为您详细介绍抖音AI分身的操作方法,助您在抖音平台上脱颖而出! 一、了解抖音AI分身 抖音AI分身是一项基于人工智能算法打造的特效功能…

    2026年9月23日
    000
  • Java岗大厂面试百日冲刺 – 日积月累,每日三题【Day25】—— JVM1

    Java岗大厂面试百日冲刺 – 日积月累,每日三题【Day25】—— JVM1Java岗大厂面试百日冲刺 – 日积月累,每日三题【Day25】—— JVM1Java岗大厂面试百日冲刺 – 日积月累,每日三题【Day25】—— JVM1Java岗大厂面试百日冲刺 – 日积月累,每日三题【Day25】—— JVM1

    车票 面试题1:你遇到过哪些OOM情况,什么原因造成的?怎么解决的? 该问题主要针对你遇到的实际问题出发,可以根据你实际遇到过的情况和场景,结合下面每种情况的具体原因和解决方式,整理后回答。 当堆内存(Heap Space)没有足够空间存放新创建的对象时,就会抛出 java.lang.OutOfMe…

    2026年9月23日 用户投稿
    000
  • 如何用PhotoPosPro的AI裁剪图片?快速实现智能裁剪的教程

    PhotoPosPro的AI裁剪功能可自动识别图片主体并裁剪边缘,适合快速处理或构图新手。打开图片后,在“Image”或“Tools”菜单中找到“AI Crop”工具,可选裁剪比例或让软件自动判断,点击“Apply”运行AI裁剪。完成后可手动微调裁剪框,满意后保存。若效果不佳,可尝试手动调整、切换A…

    2026年9月23日
    200
  • win11怎么进入BIOS设置界面_win11进入BIOS界面操作教程

    首先通过高级启动选项进入BIOS,适用于无法使用快捷键的情况:打开设置→系统→恢复→立即重新启动→疑难解答→高级选项→UEFI固件设置→重启进入BIOS;其次可在开机时按品牌特定快捷键,如联想按F2或Fn+F2、戴尔F2、惠普ESC、华硕与技嘉Del键;此外部分联想设备支持NOVO按钮,在关机状态下…

    2026年9月23日
    100
  • 为什么Java中构造方法重要 如何正确编写构造方法

    构造方法确保对象正确初始化:通过强制赋初值、校验数据、支持封装和重载提升灵活性;编写时需遵循命名一致、无返回类型、合理用参、注意访问修饰符、避免复杂逻辑及善用this()调用;常见误区包括忽略无参构造、过度初始化和异常处理不当。 构造方法在Java中扮演着初始化对象的关键角色。创建对象时,构造方法会…

    2026年9月23日
    000
  • 超高刷之外还有优秀色彩,这才是高刷 TN 屏该有的体验,HKC 神盾三代 UG25EF 深度评测

    超高刷之外还有优秀色彩,这才是高刷 TN 屏该有的体验,HKC 神盾三代 UG25EF 深度评测超高刷之外还有优秀色彩,这才是高刷 TN 屏该有的体验,HKC 神盾三代 UG25EF 深度评测超高刷之外还有优秀色彩,这才是高刷 TN 屏该有的体验,HKC 神盾三代 UG25EF 深度评测超高刷之外还有优秀色彩,这才是高刷 TN 屏该有的体验,HKC 神盾三代 UG25EF 深度评测

    引言:极致的速度真的能带来体验提升吗 在竞技类游戏中,顶尖选手与普通玩家的差距,有时就取决于毫秒之间的反应。而为了缩短这转瞬即逝的差距,玩家们选择不断升级外设,而厂商则在技术的极限上不断探索。当刷新率从 144Hz 跃升至 240Hz 时,我们感受到了前所未有的流畅;但当这个数字继续攀升,我们不禁要…

    2026年9月23日 用户投稿
    100

发表回复

登录后才能评论
关注微信