浅谈C++标准库智能指针的deleter

2025-4-6|2025-4-6
FollyCoolly
FollyCoolly
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 被设计成为一个零额外开销的智能指针,也就是说,使用它,应该相比你手工写 newdelete 没有额外开销,不管是时间还是空间上。(从实际结果来看,也基本做到了——虽然没有百分百,但也差得不多。) 因此,如何销毁对象,对 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.
如果 TB 类的派生类(即 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 可以放宽这个限制,但这是以内存开销为代价实现的。
可以看到,使用函数指针作为 deleter 的类型需要额外消耗 8 字节的内存空间,不难理解这是因为需要额外存储一个函数指针;而使用function<void(FILE*)>占用的内存空间就更夸张了。
 
 
git使用技巧博客搭建过程中的那些问题
Loading...