shared_ptr的自定义删除器使其能灵活管理非内存资源,通过lambda、函数对象或普通函数指定释放逻辑,确保文件句柄、数组等资源安全释放,实现RAII。

shared_ptr
delete
std::shared_ptr
delete
new
fopen
FILE*
fclose
CloseHandle
shared_ptr
自定义删除器可以是:
使用时,自定义删除器作为
shared_ptr
立即学习“C++免费学习笔记(深入)”;
我时常会遇到这样的场景:从某个C库获取了一个资源句柄,比如打开了一个文件得到
FILE*
std::unique_ptr
std::shared_ptr
delete
delete
new
FILE*
malloc
fclose()
它不仅仅是避免内存泄漏,更是为了实现“资源获取即初始化”(RAII)原则,将资源的生命周期管理与对象生命周期绑定。想象一下,你打开了一个文件,然后代码在某个地方抛了异常,或者提前返回了。如果没有RAII,你很可能忘记调用
fclose
shared_ptr
shared_ptr
自定义删除器的实现方式其实挺灵活的,选择哪种主要看你的具体需求和个人偏好。我个人最常用的是Lambda表达式,因为它简洁,而且可以捕获上下文变量,非常方便。
这里我们看几个具体的例子:
1. 使用Lambda表达式作为删除器 (最常用)
这种方式非常适合那些一次性的、不复杂的删除逻辑。
#include <iostream>
#include <memory>
#include <cstdio> // For FILE* operations
#include <vector> // For new int[] example
// 模拟一个C风格的资源获取和释放函数
FILE* open_my_file(const char* filename, const char* mode) {
std::cout << "Opening file: " << filename << std::endl;
return fopen(filename, mode);
}
int main() {
// 示例1: 管理文件句柄 (FILE*)
// 默认的shared_ptr会尝试delete FILE*,这显然是错的
// 我们需要用fclose来关闭文件
auto file_ptr = std::shared_ptr<FILE>(
open_my_file("example.txt", "w"),
[](FILE* f) { // 这是一个Lambda删除器
if (f) {
std::cout << "Lambda Deleter: Closing file." << std::fclose(f) << std::endl;
}
}
);
if (file_ptr) {
fprintf(file_ptr.get(), "Hello from shared_ptr with custom deleter!\n");
std::cout << "File content written." << std::endl;
} else {
std::cerr << "Failed to open file." << std::endl;
}
std::cout << "-----------------------------------" << std::endl;
// 示例2: 管理通过 new[] 分配的数组
// 默认的shared_ptr会调用 delete p; 而不是 delete[] p;
// 这会导致未定义行为,我们需要 delete[]
auto array_ptr = std::shared_ptr<int>(
new int[5]{1, 2, 3, 4, 5},
[](int* p) { // 另一个Lambda删除器
std::cout << "Lambda Deleter: Deleting int array." << std::endl;
delete[] p;
}
);
for (int i = 0; i < 5; ++i) {
std::cout << "Array element " << i << ": " << array_ptr.get()[i] << std::endl;
}
std::cout << "-----------------------------------" << std::endl;
// 示例3: Lambda捕获变量 (比如一个日志器)
std::string log_prefix = "[MyResource]";
auto resource_ptr = std::shared_ptr<void>(
nullptr, // 只是一个示例,实际中会是某个资源指针
[log_prefix](void* p) { // Lambda捕获log_prefix
std::cout << log_prefix << " Resource (void*) is being released." << std::endl;
// 实际中这里会有资源释放逻辑
}
);
// 这里的resource_ptr虽然是nullptr,但其deleter依然有效
// 当resource_ptr离开作用域时,deleter会被调用。
return 0; // file_ptr 和 array_ptr 在这里离开作用域,删除器会被调用
}2. 使用函数对象(Functor)作为删除器
当删除逻辑比较复杂,或者需要封装状态,或者希望删除器可以复用时,函数对象是一个很好的选择。
#include <iostream>
#include <memory>
#include <cstdio>
// 假设有一个自定义的日志系统
class Logger {
public:
void log(const std::string& msg) const {
std::cout << "[Logger] " << msg << std::endl;
}
};
// 函数对象删除器,用于关闭文件并记录日志
struct FileCloser {
Logger logger; // 可以包含状态
FileCloser(const Logger& l) : logger(l) {}
void operator()(FILE* f) const {
if (f) {
logger.log("Functor Deleter: Closing file.");
std::fclose(f);
}
}
};
int main() {
Logger my_logger;
auto file_ptr_functor = std::shared_ptr<FILE>(
fopen("functor_example.txt", "w"),
FileCloser(my_logger) // 传入函数对象实例
);
if (file_ptr_functor) {
fprintf(file_ptr_functor.get(), "Content from functor deleter.\n");
}
return 0;
}3. 使用普通函数作为删除器
如果删除逻辑是一个不依赖任何状态的独立函数,可以直接传递函数指针。
#include <iostream>
#include <memory>
#include <cstdio>
// 普通函数作为删除器
void close_file_global(FILE* f) {
if (f) {
std::cout << "Global Function Deleter: Closing file." << std::endl;
std::fclose(f);
}
}
int main() {
auto file_ptr_global_func = std::shared_ptr<FILE>(
fopen("global_func_example.txt", "w"),
close_file_global // 直接传递函数名,它会隐式转换为函数指针
);
if (file_ptr_global_func) {
fprintf(file_ptr_global_func.get(), "Content from global function deleter.\n");
}
return 0;
}可以看到,无论是哪种方式,核心思想都是一样的:提供一个可调用对象,它接收一个原始指针,并在
shared_ptr
虽然自定义删除器功能强大,但使用时也有些地方需要留心,否则可能会引入新的问题。这就像你拿到一把瑞士军刀,功能多是多,但用不好也可能伤到自己。
1. shared_ptr
这是一个比较隐晦但非常重要的问题。
std::shared_ptr<T>
auto lambda_deleter = [](FILE* f) { /* ... */ };
std::shared_ptr<FILE> ptr1(fopen("a.txt", "w"), lambda_deleter);
// ptr1 的实际类型是 std::shared_ptr<FILE, decltype(lambda_deleter)>如果你想把这个
ptr1
std::shared_ptr<FILE>
std::shared_ptr<FILE>
std::shared_ptr<FILE, decltype(lambda_deleter)>
std::shared_ptr<FILE>
最佳实践:
auto
shared_ptr
auto
shared_ptr
std::shared_ptr<BaseType>
std::shared_ptr<void>
std::shared_ptr<void>
static_cast
dynamic_cast
std::shared_ptr<T>
2. make_shared
std::make_shared
make_shared
new T
fopen
new
shared_ptr
// 错误示范:不能这样用
// auto ptr = std::make_shared<FILE>(fopen("test.txt", "w"), [](FILE* f){ fclose(f); });
// 正确示范:
FILE* f_raw = fopen("test.txt", "w");
auto ptr = std::shared_ptr<FILE>(f_raw, [](FILE* f){ if(f) fclose(f); });3. 删除器的异常安全性
自定义删除器在执行资源释放时,不应该抛出异常。如果删除器抛出异常,
std::terminate
shared_ptr
最佳实践:
4. 资源所有权和原始指针的生命周期
当你将一个原始指针传递给
shared_ptr
shared_ptr
delete
shared_ptr
shared_ptr
shared_ptr
shared_ptr
5. 性能考量(通常不显著)
自定义删除器,特别是复杂的函数对象或Lambda,可能会比默认的
delete
shared_ptr
在绝大多数应用中,这种开销可以忽略不计。只有在极端性能敏感的场景下,才需要考虑这方面的影响。我的经验是,为了代码的清晰、安全和健壮性,这种微小的开销是完全值得的。
总的来说,
shared_ptr
以上就是C++shared_ptr自定义删除器使用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号