在C++的资源管理场景中,智能指针默认的删除器仅能处理标准new分配的内存资源,当我们需要管理文件句柄、socket连接、第三方库分配的特殊内存等资源时,默认的释放逻辑无法适配,这时候就需要通过自定义删除器来实现专属的资源清理回调。

什么是智能指针自定义删除器
智能指针的删除器是一个可调用对象,当智能指针的引用计数归零(shared_ptr)或者自身析构(unique_ptr)时,会自动调用这个可调用对象来释放管理的资源。自定义删除器就是开发者自己定义这个可调用对象,替换默认的delete操作,从而实现任意资源的清理逻辑。
自定义删除器的核心要求
自定义删除器需要满足两个基本条件:
- 可调用:支持operator()调用语法
- 参数匹配:接受智能指针管理的资源类型的指针作为参数
unique_ptr的自定义删除器实现
unique_ptr的删除器是类型的一部分,需要在模板参数中显式指定删除器的类型,因此unique_ptr的自定义删除器需要在定义时确定。
使用函数作为删除器
我们可以先定义一个普通的释放函数,然后将其作为unique_ptr的删除器:
#include <iostream>
#include <memory>
#include <cstdio>
// 自定义文件资源释放函数
void file_deleter(FILE* fp) {
if (fp != nullptr) {
std::cout << "执行文件资源清理回调" << std::endl;
fclose(fp);
}
}
int main() {
// 定义管理FILE*的unique_ptr,指定删除器类型为函数指针类型void(*)(FILE*)
std::unique_ptr<FILE, void(*)(FILE*)> file_ptr(fopen("test.txt", "w"), file_deleter);
if (file_ptr) {
fwrite("hello", 1, 5, file_ptr.get());
}
// file_ptr析构时自动调用file_deleter释放文件资源
return 0;
}
使用lambda表达式作为删除器
lambda表达式是更常用的自定义删除器形式,代码更简洁,不需要单独定义函数:
#include <iostream>
#include <memory>
#include <cstdlib>
// 模拟第三方库的内存分配函数
void* third_party_alloc() {
return malloc(1024);
}
// 模拟第三方库的内存释放函数
void third_party_free(void* ptr) {
std::cout << "执行第三方内存资源清理回调" << std::endl;
free(ptr);
}
int main() {
// 使用lambda作为删除器,自动推导删除器类型
auto deleter = [](void* ptr) {
third_party_free(ptr);
};
std::unique_ptr<void, decltype(deleter)> mem_ptr(third_party_alloc(), deleter);
// mem_ptr析构时自动调用lambda执行资源清理
return 0;
}
使用函数对象作为删除器
函数对象(重载了operator()的类)也可以作为删除器,适合需要携带额外状态的场景:
#include <iostream>
#include <memory>
// 带状态的删除器函数对象
class SocketDeleter {
public:
SocketDeleter(int timeout) : timeout_(timeout) {}
void operator()(int* socket_fd) {
if (socket_fd != nullptr && *socket_fd != -1) {
std::cout << "执行socket资源清理回调,超时时间:" << timeout_ << std::endl;
// 模拟关闭socket的操作
close(*socket_fd);
delete socket_fd;
}
}
private:
int timeout_;
};
int main() {
int* sock = new int(10); // 模拟socket文件描述符
// 传入带参数的删除器对象
std::unique_ptr<int, SocketDeleter> sock_ptr(sock, SocketDeleter(30));
return 0;
}
shared_ptr的自定义删除器实现
shared_ptr的删除器不是类型的一部分,不需要在模板参数中指定,只需要在构造时传入可调用对象即可,使用起来比unique_ptr更灵活。
基本使用方式
shared_ptr的构造函数第二个参数可以接收任意可调用对象作为删除器:
#include <iostream>
#include <memory>
// 自定义资源类型
struct CustomResource {
int data;
};
// 自定义资源释放函数
void custom_resource_deleter(CustomResource* res) {
if (res != nullptr) {
std::cout << "执行自定义资源清理回调" << std::endl;
delete res;
}
}
int main() {
// 构造shared_ptr时传入删除器,不需要指定删除器类型
std::shared_ptr<CustomResource> res_ptr(new CustomResource{100}, custom_resource_deleter);
// 多个shared_ptr共享资源,最后一个析构时调用删除器
auto res_ptr2 = res_ptr;
return 0;
}
使用lambda作为shared_ptr删除器
shared_ptr搭配lambda使用自定义删除器非常方便:
#include <iostream>
#include <memory>
#include <cstdlib>
int main() {
// 使用lambda作为删除器,管理通过calloc分配的内存
int* arr = (int*)calloc(10, sizeof(int));
std::shared_ptr<int> arr_ptr(arr, [](int* ptr) {
std::cout << "执行calloc内存资源清理回调" << std::endl;
free(ptr);
});
return 0;
}
两种智能指针自定义删除器的区别
我们可以通过下面的表格对比unique_ptr和shared_ptr自定义删除器的差异:
| 对比项 | unique_ptr | shared_ptr |
|---|---|---|
| 删除器是否为类型一部分 | 是,需要在模板参数指定 | 否,构造时传入即可 |
| 灵活性 | 较低,定义时确定删除器类型 | 较高,不同实例可以有不同的删除器 |
| 内存开销 | 无额外开销,删除器作为类型的一部分 | 有额外开销,删除器存储在控制块中 |
| 适用场景 | 独占资源,追求零额外开销 | 共享资源,需要灵活指定删除逻辑 |
自定义删除器的注意事项
- 删除器的参数类型必须和智能指针管理的资源类型匹配,否则会导致编译错误或者运行时错误
- 删除器内部需要处理空指针的情况,避免对空指针执行释放操作
- 如果自定义删除器需要捕获上下文状态,优先使用lambda或者函数对象,避免使用全局函数
- shared_ptr的删除器不会影响其他shared_ptr实例,只有最后一个析构的实例会调用删除器
总结
自定义删除器是C++智能指针的重要扩展能力,让智能指针可以管理任意类型的资源,实现资源清理回调。unique_ptr的自定义删除器需要在模板参数中指定类型,适合独占资源且追求零开销的场景;shared_ptr的自定义删除器更灵活,适合共享资源的场景。开发者可以根据实际的资源管理需求选择合适的智能指针和删除器实现方式,避免资源泄漏,提升代码的健壮性。
C++智能指针自定义删除器资源清理unique_ptrshared_ptr修改时间:2026-07-16 08:51:33