C++智能指针通过自定义删除器实现资源释放,unique_ptr在模板中指定删除器类型,适用于独占资源管理;shared_ptr将删除器作为构造参数,支持共享资源的不同释放策略,二者均扩展了RAII的应用范围。

在C++中,智能指针实现自定义资源释放的核心在于为其提供一个“删除器”(Deleter),这个删除器是一个可调用对象,负责在智能指针所管理的资源不再被引用时,执行特定的清理操作,而非默认的
delete
要为C++智能指针实现自定义资源释放,你需要根据所使用的智能指针类型(
std::unique_ptr
std::shared_ptr
对于 std::unique_ptr
std::unique_ptr
#include <iostream>
#include <memory>
#include <cstdio> // For FILE*
// 1. 使用函数对象作为删除器
struct FileCloser {
void operator()(FILE* fp) const {
if (fp) {
std::cout << "Closing file via functor..." << std::endl;
fclose(fp);
}
}
};
// 2. 使用普通函数作为删除器
void custom_file_deleter(FILE* fp) {
if (fp) {
std::cout << "Closing file via function pointer..." << std::endl;
fclose(fp);
}
}
int main() {
// 使用函数对象作为删除器
std::unique_ptr<FILE, FileCloser> file1(fopen("test1.txt", "w"));
if (file1) {
fprintf(file1.get(), "Hello from file1!\n");
}
// 使用lambda表达式作为删除器 (更常见和推荐)
auto lambda_file_closer = [](FILE* fp) {
if (fp) {
std::cout << "Closing file via lambda..." << std::endl;
fclose(fp);
}
};
std::unique_ptr<FILE, decltype(lambda_file_closer)> file2(fopen("test2.txt", "w"), lambda_file_closer);
if (file2) {
fprintf(file2.get(), "Hello from file2!\n");
}
// 使用函数指针作为删除器
std::unique_ptr<FILE, decltype(&custom_file_deleter)> file3(fopen("test3.txt", "w"), &custom_file_deleter);
if (file3) {
fprintf(file3.get(), "Hello from file3!\n");
}
std::cout << "Unique pointers will now go out of scope and release resources." << std::endl;
return 0;
}对于 std::shared_ptr
std::shared_ptr
std::shared_ptr
立即学习“C++免费学习笔记(深入)”;
#include <iostream>
#include <memory>
#include <string>
// 假设我们有一个自定义的资源类型,需要特定的释放逻辑
struct CustomResource {
std::string name;
CustomResource(const std::string& n) : name(n) {
std::cout << "CustomResource " << name << " acquired." << std::endl;
}
void do_something() {
std::cout << "CustomResource " << name << " doing something." << std::endl;
}
};
// 自定义删除器函数
void custom_resource_deleter(CustomResource* res) {
if (res) {
std::cout << "Releasing CustomResource " << res->name << " via custom deleter." << std::endl;
delete res; // 仍然需要释放内存,但可能在释放前做其他事情
}
}
int main() {
// 使用lambda表达式作为删除器
std::shared_ptr<CustomResource> res1(new CustomResource("DB_Connection"), [](CustomResource* r) {
std::cout << "Closing DB_Connection " << r->name << " via lambda." << std::endl;
delete r;
});
res1->do_something();
// 使用函数指针作为删除器
std::shared_ptr<CustomResource> res2(new CustomResource("Network_Socket"), custom_resource_deleter);
res2->do_something();
// 即使是同一个类型,也可以有不同的删除器,但 shared_ptr 的类型保持不变
std::shared_ptr<CustomResource> res3(new CustomResource("File_Handle"), [](CustomResource* r) {
std::cout << "Flushing and closing File_Handle " << r->name << " via another lambda." << std::endl;
delete r;
});
res3->do_something();
std::cout << "Shared pointers will now go out of scope and release resources." << std::endl;
return 0;
}std::unique_ptr
new
unique_ptr
我个人认为,
unique_ptr
举个例子,操作系统的许多API都返回需要特定函数(而非
delete
HANDLE
FILE*
DIR*
pthread_mutex_t
CloseHandle
fclose
closedir
pthread_mutex_destroy
unique_ptr
unique_ptr
#include <iostream>
#include <memory>
#include <windows.h> // 假设在Windows环境
// 定义一个用于关闭HANDLE的删除器
struct HandleCloser {
void operator()(HANDLE h) const {
if (h != INVALID_HANDLE_VALUE && h != nullptr) {
std::cout << "Closing Windows HANDLE..." << std::endl;
CloseHandle(h);
}
}
};
int main() {
// 假设我们打开一个事件对象
// std::unique_ptr<HANDLE, HandleCloser> event_handle(CreateEvent(NULL, TRUE, FALSE, NULL));
// 注意:CreateEvent返回HANDLE* 而不是HANDLE,且 unique_ptr 默认期望 T*
// 更正确的做法是 unique_ptr<void, HandleCloser> 或直接 unique_ptr<HANDLE, HandleCloser>
// 但这里为了简化,我们假设 CreateEvent 返回一个可以直接被 HandleCloser 处理的类型
// 实际使用时,可能需要对返回类型进行适配,或者直接用 HANDLE 类型
// 这里用一个简单的 int* 模拟一个需要自定义释放的资源
auto custom_int_deleter = [](int* p) {
std::cout << "Deleting custom int array." << std::endl;
delete[] p;
};
std::unique_ptr<int[], decltype(custom_int_deleter)> my_array(new int[10], custom_int_deleter);
my_array[0] = 100;
std::cout << "my_array[0]: " << my_array[0] << std::endl;
// 真正的HANDLE例子可能更像这样:
// std::unique_ptr<void, HandleCloser> hFile(
// CreateFile("example.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),
// HandleCloser{}
// );
// if (hFile.get() == INVALID_HANDLE_VALUE) {
// std::cerr << "Failed to open file." << std::endl;
// } else {
// std::cout << "File handle acquired successfully." << std::endl;
// }
std::cout << "Resources will be released now." << std::endl;
return 0;
}可以看到,
unique_ptr
unique_ptr
std::shared_ptr
unique_ptr
shared_ptr
std::shared_ptr<T>
std::shared_ptr<T>
考虑一个场景:一个数据库连接池。当一个连接从池中取出时,它被包装在一个
shared_ptr
shared_ptr
shared_ptr
delete
returnConnection
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <mutex> // for thread safety
// 模拟一个数据库连接
struct DBConnection {
std::string id;
bool is_open = true;
DBConnection(const std::string& _id) : id(_id) {
std::cout << "DBConnection " << id << " opened." << std::endl;
}
void query(const std::string& sql) {
if (is_open) {
std::cout << "Connection " << id << " executing: " << sql << std::endl;
} else {
std::cout << "Connection " << id << " is closed, cannot query." << std::endl;
}
}
void close() {
if (is_open) {
std::cout << "DBConnection " << id << " truly closed." << std::endl;
is_open = false;
}
}
~DBConnection() {
std::cout << "DBConnection " << id << " destructor called." << std::endl;
}
};
// 模拟一个连接池
class ConnectionPool {
private:
std::vector<std::shared_ptr<DBConnection>> available_connections;
std::mutex mtx;
int next_id = 0;
public:
ConnectionPool() {
// 预创建一些连接
for (int i = 0; i < 3; ++i) {
available_connections.push_back(std::make_shared<DBConnection>("conn_" + std::to_string(next_id++)));
}
std::cout << "ConnectionPool initialized with " << available_connections.size() << " connections." << std::endl;
}
// 获取一个连接
std::shared_ptr<DBConnection> getConnection() {
std::lock_guard<std::mutex> lock(mtx);
if (available_connections.empty()) {
std::cout << "Pool empty, creating new connection." << std::endl;
// 正常情况下,这里会阻塞等待或抛异常
// 简化处理,直接创建一个新的并加入池中
available_connections.push_back(std::make_shared<DBConnection>("conn_" + std::to_string(next_id++)));
}
std::shared_ptr<DBConnection> conn = available_connections.back();
available_connections.pop_back();
// 为这个连接设置一个自定义删除器,当引用计数为0时,将连接归还到池中
// 注意:这里需要捕获 this 指针,确保删除器能访问到 ConnectionPool 实例
// 且删除器本身不能是 ConnectionPool 的成员函数,因为 shared_ptr 期望一个裸指针作为参数
return std::shared_ptr<DBConnection>(conn.get(), [this](DBConnection* c) {
std::lock_guard<std::mutex> lock_deleter(mtx);
std::cout << "Returning DBConnection " << c->id << " to pool." << std::endl;
// 确保归还的连接是打开的,如果业务逻辑中可能关闭,这里需要重新打开或检查状态
// c->is_open = true; // 假设归还时总是可用的
available_connections.push_back(std::shared_ptr<DBConnection>(c)); // 重新包装成shared_ptr
});
}
~ConnectionPool() {
std::cout << "ConnectionPool shutting down. Truly closing all " << available_connections.size() << " connections." << std::endl;
for (auto& conn : available_connections) {
conn->close();
}
}
};
int main() {
ConnectionPool pool;
{
std::shared_ptr<DBConnection> conn1 = pool.getConnection();
conn1->query("SELECT * FROM users;");
std::shared_ptr<DBConnection> conn2 = pool.getConnection();
conn2->query("INSERT INTO products VALUES (...);");
// conn1 和 conn2 在这里超出作用域,它们的自定义删除器会被调用,将连接归还到池中
} // conn1, conn2 out of scope here
std::cout << "\nAfter connections returned to pool." << std::endl;
{
std::shared_ptr<DBConnection> conn3 = pool.getConnection();
conn3->query("UPDATE orders SET status='shipped';");
} // conn3 out of scope
std::cout << "\nMain function ending." << std::endl;
return 0;
}在这个例子中,
shared_ptr
this
ConnectionPool
自定义删除器在实际项目中确实是解决复杂资源管理问题的利器,但它也伴随着一些需要注意的挑战和高级应用场景。
高级应用场景:
C-style API的RAII封装: 这可能是最常见的应用。例如,
libcurl
sqlite3
OpenSSL
xxx_init()
xxx_cleanup()
xxx_free()
// 假设有一个简单的C库函数
void* create_my_context() { std::cout << "Context created." << std::endl; return new int; }
void destroy_my_context(void* ctx) { std::cout << "Context destroyed." << std::endl; delete (int*)ctx; }
std::unique_ptr<void, decltype(&destroy_my_context)> ctx_ptr(create_my_context(), &destroy_my_context);跨DLL/SO的内存管理: 在Windows上,如果一个DLL使用
new
delete
// DLL_A.h extern "C" __declspec(dllexport) MyObject* create_object(); extern "C" __declspec(dllexport) void destroy_object(MyObject* obj); // main.cpp std::unique_ptr<MyObject, decltype(&destroy_object)> my_obj_ptr(create_object(), &destroy_object);
自定义内存分配器集成: 如果你的项目使用了自定义的内存分配器(例如,一个高性能的池式分配器),你可以为智能指针提供一个删除器,让它调用你的分配器的
deallocate
delete
template<typename T>
struct MyAllocatorDeleter {
MyCustomAllocator* allocator_ptr; // 存储分配器实例的指针
MyAllocatorDeleter(MyCustomAllocator* alloc) : allocator_ptr(alloc) {}
void operator()(T* p) const {
if (p && allocator_ptr) {
allocator_ptr->deallocate(p);
}
}
};
// 使用时
MyCustomAllocator my_alloc;
std::unique_ptr<MyData, MyAllocatorDeleter<MyData>> data_ptr(
my_alloc.allocate<MyData>(), MyAllocatorDeleter<MyData>(&my_alloc)
);资源归还而非销毁: 像前面数据库连接池的例子,删除器不销毁资源,而是将其归还到某个池中。这对于任何可复用的昂贵资源都非常有用。
潜在挑战:
unique_ptr
unique_ptr
unique_ptr
std::function
// 假设我们有多个不同的自定义分配器实例 MyCustomAllocator alloc1, alloc2; // unique_
以上就是C++如何在智能指针中实现自定义资源释放的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号