C++ std::unique_ptr 和 std::shared_ptr 都支持自定义删除器(deleter)
在 C++ 中,std::unique_ptr 和 std::shared_ptr 都支持自定义删除器(deleter),但它们在实现和使用上有一些关键区别。以下是它们的异同点:
相同点
-
支持自定义删除器
两者都允许通过模板参数或构造函数参数指定删除器,用于管理资源释放(如文件句柄、内存、网络连接等)。 -
默认删除器行为相同
如果不指定删除器,两者默认使用delete或delete[](针对数组版本)释放资源。
不同点
1. 删除器的存储方式
-
std::unique_ptr- 删除器是类型的一部分(通过模板参数指定),存储在智能指针对象内部。
- 如果未指定删除器,默认使用
std::default_delete<T>。 - 由于删除器是类型的一部分,不同删除器的
unique_ptr是不同类型,不能直接互相赋值或传递。 - 示例:
std::unique_ptr<FILE, decltype(&fclose)> file(fopen("test.txt", "r"), &fclose);
-
std::shared_ptr- 删除器不是类型的一部分,而是通过构造函数参数动态绑定,存储在控制块中。
- 无论删除器如何,
shared_ptr的类型始终是std::shared_ptr<T>,因此不同删除器的shared_ptr可以互相赋值或传递。 - 示例:
std::shared_ptr<FILE> file(fopen("test.txt", "r"), &fclose);
2. 性能影响
-
std::unique_ptr- 删除器是编译期确定的,无运行时开销(可能被内联优化)。
- 对象大小可能因删除器类型而变化(例如函数指针会增加指针大小)。
-
std::shared_ptr- 删除器存储在动态分配的控制块中,可能带来轻微运行时开销。
- 对象大小固定(通常为两个指针,指向对象和控制块)。
3. 灵活性与类型擦除
-
std::unique_ptr- 删除器是类型的一部分,灵活性较低(需要显式指定模板参数)。
- 适合简单场景或需要极致性能的情况。
-
std::shared_ptr- 删除器通过类型擦除(type erasure)实现,灵活性更高。
- 可以捕获任意可调用对象(如函数、lambda、仿函数),无需在类型中显式声明。
- 示例:
auto deleter = [](FILE* f) { fclose(f); }; std::shared_ptr<FILE> file(fopen("test.txt", "r"), deleter);
4. 数组支持
-
std::unique_ptr- 显式支持数组形式(
std::unique_ptr<T[]>),默认调用delete[]。 - 示例:
std::unique_ptr<int[]> arr(new int[10]);
- 显式支持数组形式(
-
std::shared_ptr- 不直接支持数组形式(C++17 前需手动指定删除器
delete[])。 - C++17 起可用
std::default_delete<T[]>或std::make_shared的扩展。 - 示例:
std::shared_ptr<int> arr(new int[10], std::default_delete<int[]>());
- 不直接支持数组形式(C++17 前需手动指定删除器
总结表格
| 特性 | std::unique_ptr |
std::shared_ptr |
|---|---|---|
| 删除器存储位置 | 编译期确定(类型的一部分) | 运行时动态存储(控制块中) |
| 类型影响 | 删除器不同则类型不同 | 删除器不影响类型 |
| 性能 | 无额外开销 | 轻微运行时开销 |
| 灵活性 | 需显式指定模板参数 | 支持任意可调用对象 |
| 数组支持 | 直接支持(unique_ptr<T[]>) |
需手动指定删除器(如 default_delete<T[]>) |
使用建议
- 优先用
std::unique_ptr,除非需要共享所有权。 - 需要灵活删除器时(如捕获状态的 lambda),选择
std::shared_ptr。 - 数组资源建议用
std::unique_ptr<T[]>或 C++17 的std::shared_ptr改进。
浙公网安备 33010602011771号