C++: 智能指针的自定义删除器 `Custom Deleter` 有什么用?
C++11智能指针std::shared_ptr
和std::unique_ptr
都支持自定义删除器,本文将介绍自定义删除器的使用场景和使用方法。智能指针模板参数的第二个类型是删除器,一般是一个函数指针类型或者是一个函数对象类型。通常情况下,删除器的类型是std::default_delete<T>
,它是一个函数对象类型,用于调用delete
来释放所管理的对象。
template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr;
template <typename T, typename Deleter = std::default_delete<T>>
class shared_ptr;
1. 自定义删除器的使用场景
自定义删除器的作用是在智能指针释放所管理的对象时,执行一些特殊的操作,比如:
内存释放时打印一些日志。
管理除内存以外的其它资源,例如文件句柄、数据库连接等。
与自定义分配器(Allocator)配合使用,将资源释放给自定义分配器。
在C++17之前,
std::shared_ptr
用于管理数组时需要自定义删除器来释放数组内存,因为默认使用delete
来释放所管理的对象,而delete
不能正确释放分配的数组,需要在自定义删除器delete[]
释放数组。
注: C++17 之后,
std::shared_ptr
可以管理动态分配的数组,因为std::shared_ptr<T[]>
默认使用delete[]
来释放所管理的对象。
2. 自定义删除器的使用
自定义删除器可以是一个函数,也可以是一个类的对象, 也可以是一个lambda表达式。
如果是一个函数,它的形式如下:
void free_memory(int* p) {
std::cout << "delete memory" << std::endl;
delete p;
}
如果是一个类的对象,它的形式如下:
class FreeMemory {
public:
void operator()(int* p) {
std::cout << "delete memory" << std::endl;
delete p;
}
};
如果是一个lambda表达式,它的形式如下:
auto free_memory_lambda = [](int* p) {
std::cout << "delete memory" << std::endl;
delete p;
}
2.1 shared_ptr自定义删除器的使用:
对于shared_ptr, 不管删除器什么类型,是否有状态都不会增加shared_ptr的大小, 均为两个字长。因为删除器是存储在控制块中,而控制块的大小为两个字长。
std::shared_ptr<int> sp1(new int(0), free_memory); // size: 8
std::shared_ptr<int> sp2(new int(0), FreeMemory()); // size: 8
std::shared_ptr<int> sp3(new int(0), free_memory_lambda); // size: 8
2.2 unique_ptr自定义删除器的使用:
- unique_ptr的删除器类型是一个模板参数,因此需要指定删除器类型。
- 如果删除器是函数指针类型,
std::unique_ptr
大小从1个字长增长到2个字长,因为需要存储函数指针。 - 如果删除器是无状态删除器(stateless function),比如不进行捕获的lambda表达式,
std::unique_ptr
大小不变,因为无状态删除器不需要存储任何成员变量。
std::unique_ptr<int, FreeMemory> up1(new int(0)); // size: 4
std::unique_ptr<int, void(*)(int*)> up2(new int(0), free_memory); // size: 8
std::unique_ptr<int, decltype(free_memory)*> up3(new int(0), free_memory); // size: 4
3. 有状态删除器和无状态删除器
什么是有状态删除器?什么是无状态删除器?有状态删除器是指删除器类中包含有成员变量,无状态删除器是指删除器类中不包含有成员变量。
如果std::unique_ptr
的函数对象删除器是具有扩展状态的,其大小可能会非常大。如果大得无法接受,可能需要设计一个无状态删除器。
下面是一个有状态删除器的例子:
class DeleteObject {
public:
DeleteObject(int n) : n_(n) {}
void operator()(int* p) {
std::cout << "delete memory " << n_ << std::endl;
delete p;
}
private:
int n_;
};
总结
本文介绍了自定义删除器的使用场景和使用方法,以及有状态删除器和无状态删除器的区别。帮助大家了解自定义删除器的使用方法。
参考
- Effective Modern C++, Item 18: Use
std::unique_ptr
for exclusive-ownership resource management.
你好,我是七昂,计算机科学爱好者,致力于分享C/C++、操作系统、软件架构等计算机基础知识。希望我们能一起探索程序员修炼之道,最终能站得更高,走得更远。如果你有任何问题或者建议,欢迎随时与我交流。如果我的创作内容对您有帮助,请点赞关注。感谢你的阅读。
C++: 智能指针的自定义删除器 `Custom Deleter` 有什么用?的更多相关文章
- C11内存管理之道:智能指针
1.shared_ptr共享智能指针 std::shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候,内存才会释放. 1.1 基本 ...
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- 智能指针剖析(下)boost::shared_ptr&其他
1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...
- C++智能指针剖析(下)boost::shared_ptr&其他
1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...
- c/c++ 智能指针 unique_ptr 使用
智能指针 unique_ptr 使用 和shared_ptr不同,可以有多个shared_ptr指向同一个内存,只能有1个unique_ptr指向某个内存.因此unique_ptr不支持普通的拷贝和赋 ...
- c/c++ 智能指针 shared_ptr 和 new结合使用
智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...
- C++ 智能指针六
/* 智能指针unique_ptr */ #include <iostream> #include <string> #include <memory> #incl ...
- C++ 智能指针二
/* 智能指针shared_ptr注意点 */ #include <iostream> #include <string> #include <memory> // ...
- C++智能指针 unique_ptr
C++智能指针 unique_ptr unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory ...
- 第21课 shared_ptr共享型智能指针
一. shared_ptr的基本用法 (一)与unique_ptr的比较 比较 shared_ptr unique_ptr 备注 初始化 ①shared_ptr<T> sp; sp.res ...
随机推荐
- 第二部分:关键技术领域的开源实践【Linux服务器自动化运维】
Linux运维可能会遇到多种问题,这些问题可能源于技术挑战.资源配置.安全性.管理复杂性等多个方面.以下是一些常见的Linux运维问题: 技能要求: Linux系统通常需要较高的技术水平和经验来进行有 ...
- Nginx使用upstream实现动静分离
一.为什么要进行动静分离 分离资源,减少不必要到的请求消耗,减少请求延时. 注:我这里,是nginx处理静态资源,apache处理动态资源. 场景分析: 1.未分离之前的场景步骤 (1)客户端请求ur ...
- oeasy教您玩转vim - 65 - # 批处理操作
批处理操作 回忆上次 我们上次参数列表 arguments list 所谓参数列表指的是 vim 打开的 参数列表 参数会加载到内存中成为 buffer 参数的控制 :arga filename ...
- 服务端渲染中的数据获取:结合 useRequestHeaders 与 useFetch
title: 服务端渲染中的数据获取:结合 useRequestHeaders 与 useFetch date: 2024/7/24 updated: 2024/7/24 author: cmdrag ...
- android实现多线程基础
//创建线程类 class Mythread extends Thread{ @Override public void run(){ //定义行为 } } //实例化线程类 MyThread mt= ...
- ipa文件上传到app store的构建版本的工具
打包好ipa文件后,可以使用mac电脑上的xcode将ipa上传到app store的构建版本中,假如没有mac电脑,可以使用香蕉云编来将ipa文件上传到构建版本. 这里我们来介绍下ipa文件上传到a ...
- .NET周刊【7月第4期 2024-07-28】
国内文章 .NET 高性能缓冲队列实现 BufferQueue https://mp.weixin.qq.com/s/fUhJpyPqwcmb3whuV3CDyg BufferQueue 是一个用 . ...
- 认识netty的基本组件
Java NIO VS Netty 有了 Java NIO,而且 Netty 也是基于 Java NIO 实现,那么为什么不能直接用 Java NIO 来实现网络通信模块呢? 接下来我就给大家解释一下 ...
- 【Centos】RPM安装Mysql8
先去官网下载RPM包,没想到RPM包是红帽发行版 https://dev.mysql.com/downloads/mysql/ 使用wget直接下载到Centos里面: wget https://cd ...
- 【Vue】08 Webpack Part4 使用Vue
新建一个目录创建Vue项目: cd .. mkdir webpack-vue cd webpack-vue 安装Vue cnpm install vue 保持之前的目录结构: 在Main.js中引用V ...