C++11智能指针std::shared_ptrstd::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_;
};

总结

本文介绍了自定义删除器的使用场景和使用方法,以及有状态删除器和无状态删除器的区别。帮助大家了解自定义删除器的使用方法。

参考

  1. Effective Modern C++, Item 18: Use std::unique_ptr for exclusive-ownership resource management.

你好,我是七昂,计算机科学爱好者,致力于分享C/C++、操作系统、软件架构等计算机基础知识。希望我们能一起探索程序员修炼之道,最终能站得更高,走得更远。如果你有任何问题或者建议,欢迎随时与我交流。如果我的创作内容对您有帮助,请点赞关注。感谢你的阅读。

C++: 智能指针的自定义删除器 `Custom Deleter` 有什么用?的更多相关文章

  1. C11内存管理之道:智能指针

    1.shared_ptr共享智能指针 std::shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候,内存才会释放. 1.1 基本 ...

  2. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  3. 智能指针剖析(下)boost::shared_ptr&其他

    1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...

  4. C++智能指针剖析(下)boost::shared_ptr&其他

    1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...

  5. c/c++ 智能指针 unique_ptr 使用

    智能指针 unique_ptr 使用 和shared_ptr不同,可以有多个shared_ptr指向同一个内存,只能有1个unique_ptr指向某个内存.因此unique_ptr不支持普通的拷贝和赋 ...

  6. c/c++ 智能指针 shared_ptr 和 new结合使用

    智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...

  7. C++ 智能指针六

    /* 智能指针unique_ptr */ #include <iostream> #include <string> #include <memory> #incl ...

  8. C++ 智能指针二

    /* 智能指针shared_ptr注意点 */ #include <iostream> #include <string> #include <memory> // ...

  9. C++智能指针 unique_ptr

    C++智能指针 unique_ptr unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory ...

  10. 第21课 shared_ptr共享型智能指针

    一. shared_ptr的基本用法 (一)与unique_ptr的比较 比较 shared_ptr unique_ptr 备注 初始化 ①shared_ptr<T> sp; sp.res ...

随机推荐

  1. WPF单行TextBox自动滚动至末尾

    根据光标位置自动滚动 textBox.CaretIndex = textBox.SelectionStart; var rect = textBox.GetRectFromCharacterIndex ...

  2. P1387

    #include<iostream> #include<utility> using namespace std; typedef long long ll; #define ...

  3. 9. 嵌套的 CMake

    9. 嵌套的 CMake 如果项目很大,或者项目中有很多的源码目录,在通过 CMake 管理项目的时候如果只使用一个 CMakeLists.txt ,那么这个文件相对会比较复杂,有一种化繁为简的方式就 ...

  4. oeasy教您玩转vim - 5 - # 插入模式

    插入模式 回忆上节课内容 我们总结了,模式切换的方式 命令模式 Normal mode 底线命令行模式 Command mode 帮助文件的正确打开方式 :h 在文档中使用鼠标 set mouse=a ...

  5. C#:进程之间传递数据

    一.思路 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.常用的方法有 使用内存映射文件 通过共享内存DLL共享内存 使用SendMessage向另一进程发送WM_COPYDATA ...

  6. Python 基于Python生成短8位唯一id解决方案

    基于Python生成短8位唯一id解决方案 by:授客 QQ:1033553122 测试环境: Win10 Python 3.5.4   实现思路 利用62个可打印字符,通过随机生成32位UUID,由 ...

  7. 计算机网络中的检验和(checksum)(包括计算文件的检验和附有c++代码)

    介绍: 检验和(checksum),在数据处理和数据通信领域中,用于校验目的地一组数据项的和.它通常是以十六进制为数制表示的形式.如果校验和的数值超过十六进制的FF,也就是255. 就要求其补码作为校 ...

  8. mysql 忘记root密码怎么办?

    忘记root可以跳过grant table来登录 1.打开命令行输入以下命令 mysqld -nt --grant-skip-tables 2.在打开一个新命令行,输入以下命令可以登录, mysql ...

  9. fasterWhisper和MoneyPrinterPlus无缝集成

    MoneyPrinterPlus之前使用的是各种云厂商的语音识别服务来进行语音的视频和字幕的识别工作. 但是很多小伙伴说云服务用不起. 那么没办法,MoneyPrinterPlus上线最新版本,支持f ...

  10. 2024-08-03:用go语言,给定一个从 0 开始的字符串数组 `words`, 我们定义一个名为 `isPrefixAndSuffix` 的布尔函数,该函数接受两个字符串参数 `str1` 和

    2024-08-03:用go语言,给定一个从 0 开始的字符串数组 words, 我们定义一个名为 isPrefixAndSuffix 的布尔函数,该函数接受两个字符串参数 str1 和 str2. ...