type
status
date
slug
summary
tags
category
icon
password
什么是 deleter
Deleter 是一个可调用对象(函数、函数对象、Lambda 表达式等),用于定义在智能指针生命周期结束时如何释放其管理的资源。它扩展了智能指针的功能,使其不仅限于释放动态内存,还能管理文件句柄、网络连接、第三方库资源等。
deleter 的作用
- 资源释放定制化:替换默认的
delete
操作,适应不同的资源类型。
- 资源管理解耦:将资源的释放逻辑与资源的使用分离,提升代码灵活性和可维护性。
shared_ptr 和 unique_ptr 中 deleter 的用法
unique_ptr
用法
- Deleter类型是
unique_ptr
类型的一部分,需在模板中声明。
- 示例:管理文件句柄的
unique_ptr
。
类型安全
不同 deleter 的
unique_ptr
是不同类型,无法直接赋值或比较。deleter 的开销
若 deleter 是无状态的(如无捕获的Lambda),编译器通过空基类优化(EBCO)避免内存占用。
shared_ptr
用法
- Deleter通过类型擦除(Type Erasure)存储在控制块中,不影响
shared_ptr<T>
的类型。
- 示例:管理
OpenCV
矩阵。
开销
- 控制块需额外存储Deleter副本,增加内存占用。
对比
特性 | std::unique_ptr<T, Deleter> | std::shared_ptr<T> |
Deleter存储位置 | 作为智能指针类型的一部分(编译时确定) | 存储在控制块中(运行时动态绑定) |
类型影响 | 不同Deleter导致不同类型 | 不影响智能指针类型(类型擦除) |
内存开销 | 通常零开销(空基类优化) | 控制块额外存储Deleter(增加内存占用) |
适用场景 | 独占所有权、轻量级资源管理 | 共享所有权、需跨模块传递的资源 |
unique_ptr
被设计成为一个零额外开销的智能指针,也就是说,使用它,应该相比你手工写new
和delete
没有额外开销,不管是时间还是空间上。(从实际结果来看,也基本做到了——虽然没有百分百,但也差得不多。) 因此,如何销毁对象,对unique_ptr
来讲,默认作为一个静态信息存放在模板的参数里。在使用静态删除器时,它的动态信息,每对象的空间开销,则只有一个指针的大小。 删除器的额外开销对shared_ptr
来讲则不是大问题:本来就需要额外分配内存放控制块,再多点信息也就没什么大不了了。 c++的智能指针中 unique_ptr 为什么比 shared_ptr 多一个模板参数? - 知乎
deleter 与继承
If T is a derived class of some base B, then unique_ptr<T> is implicitly convertible to unique_ptr<B>. The default deleter of the resulting unique_ptr<B> will use operator delete for B, leading to undefined behavior unless the destructor of B is virtual. Note that std::shared_ptr behaves differently: std::shared_ptr<B> will use the operator delete for the type T and the owned object will be deleted correctly even if the destructor of B is not virtual.
如果
T
是 B
类的派生类(即 T
继承自 B
),那么 unique_ptr<T>
可以隐式转换为 unique_ptr<B>
。这意味着你可以把一个指向 T
类型对象的 unique_ptr
赋值给一个指向 B
类型对象的 unique_ptr
。联想到 shared_ptr 和 unique_ptr 的底层实现,不难理解它们有如下特点:
unique_ptr<T>
到unique_ptr<B>
转换时,如果B
的析构函数不是虚拟的,会导致未定义行为,因为它不会调用T
的析构函数。
shared_ptr<B>
会正确删除T
类型的对象,即使B
的析构函数不是虚拟的,这使得它在多态删除时表现得更加安全。
测试 unique_ptr 中 deleter
不同Deleter的
unique_ptr
是不同类型,无法直接赋值或比较。将 deleter 类型设置成函数指针和 std::function
可以放宽这个限制,但这是以内存开销为代价实现的。参考c++的智能指针中 unique_ptr 为什么比 shared_ptr 多一个模板参数? - 知乎,使用以下代码进行测试:
可以看到,使用函数指针作为 deleter 的类型需要额外消耗 8 字节的内存空间,不难理解这是因为需要额外存储一个函数指针;而使用
function<void(FILE*)>
占用的内存空间就更夸张了。