自定义删除器使智能指针能管理文件句柄、网络连接等非内存资源,通过RAII确保资源安全释放,提升代码健壮性与通用性。

C++智能指针的自定义删除器,本质上就是为智能指针提供一个“如何释放”的特殊指令,让它在管理内存之外,还能妥善处理文件句柄、网络连接或其他需要特定清理流程的资源。这使得智能指针的应用范围大大扩展,从单纯的内存管理工具升级为通用的资源管理利器,让资源管理变得更加自动化、安全且富有弹性。
在C++中,智能指针(主要是
std::unique_ptr
std::shared_ptr
delete
new
fclose
CloseHandle
free
我们通常有几种方式来定义这个“清理指令”:函数对象(Functor)、Lambda表达式或者普通的函数指针。
1. 使用std::unique_ptr
立即学习“C++免费学习笔记(深入)”;
std::unique_ptr
unique_ptr
函数对象(Functor)作为删除器: 当清理逻辑比较复杂,或者希望删除器能携带一些状态时,函数对象是个不错的选择。
#include <iostream>
#include <memory>
#include <cstdio> // For FILE*
// 定义一个文件关闭器,它是一个函数对象
struct FileCloser {
void operator()(FILE* fp) const {
if (fp) {
std::cout << "DEBUG: Closing file handle via FileCloser." << std::endl;
fclose(fp);
}
}
};
void demo_unique_ptr_functor() {
// unique_ptr的第二个模板参数需要指定删除器的类型
std::unique_ptr<FILE, FileCloser> logFile(fopen("app.log", "w"));
if (logFile) {
fprintf(logFile.get(), "Application started.\n");
// ... 更多操作 ...
}
std::cout << "INFO: logFile unique_ptr scope ending." << std::endl;
// 当logFile超出作用域时,FileCloser::operator()会被调用
}Lambda表达式作为删除器: 对于一次性、局部性的清理任务,Lambda表达式非常简洁高效,尤其当它需要捕获一些上下文变量时。
#include <iostream>
#include <memory>
#include <thread> // For std::jthread example
#include <utility> // For std::move
// 假设我们有一个需要join的线程
void demo_unique_ptr_lambda_thread() {
auto thread_deleter = [](std::jthread* t) {
if (t && t->joinable()) {
std::cout << "DEBUG: Joining thread via lambda deleter." << std::endl;
t->join();
delete t; // 别忘了释放线程对象本身
} else if (t) {
delete t;
}
};
// unique_ptr的第二个模板参数需要通过decltype获取lambda的类型
std::unique_ptr<std::jthread, decltype(thread_deleter)>
worker(new std::jthread([]{
std::cout << "Worker thread running..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Worker thread finished." << std::endl;
}), thread_deleter);
std::cout << "INFO: Main thread waiting for worker." << std::endl;
// worker超出作用域时,lambda会被调用,确保线程被join
}函数指针作为删除器: 最简单直接的方式,适用于无状态的全局或静态清理函数。
#include <iostream>
#include <memory>
#include <cstdlib> // For free
// 一个通用的free函数,适用于通过malloc分配的内存
void free_deleter(void* ptr) {
if (ptr) {
std::cout << "DEBUG: Calling free() via function pointer deleter." << std::endl;
free(ptr);
}
}
void demo_unique_ptr_func_ptr() {
// unique_ptr的第二个模板参数是函数指针类型
std::unique_ptr<int, decltype(&free_deleter)> data(
static_cast<int*>(malloc(sizeof(int))), &free_deleter);
if (data) {
*data = 42;
std::cout << "Data allocated with malloc: " << *data << std::endl;
}
std::cout << "INFO: data unique_ptr scope ending." << std::endl;
// data超出作用域时,free_deleter会被调用
}2. 使用std::shared_ptr
std::shared_ptr
unique_ptr
shared_ptr<T>
std::shared_ptr<T>
shared_ptr
#include <iostream>
#include <memory>
#include <string>
#include <fstream> // For std::ofstream
// 假设我们有一个自定义的资源类,需要特定的关闭方法
class NetworkConnection {
std::string _host;
int _port;
bool _isOpen = false;
public:
NetworkConnection(const std::string& host, int port) : _host(host), _port(port) {
std::cout << "NetworkConnection to " << _host << ":" << _port << " established." << std::endl;
_isOpen = true;
}
void send(const std::string& data) {
if (_isOpen) {
std::cout << "Sending data to " << _host << ": " << data << std::endl;
}
}
void close() {
if (_isOpen) {
std::cout << "NetworkConnection to " << _host << ":" << _port << " closed." << std::endl;
_isOpen = false;
}
}
// 析构函数,但我们希望通过自定义删除器来调用close()
~NetworkConnection() {
std::cout << "NetworkConnection destructor called (should be after custom close)." << std::endl;
}
};
void demo_shared_ptr_custom_resource() {
// shared_ptr在构造时直接传入删除器,类型是std::shared_ptr<NetworkConnection>
std::shared_ptr<NetworkConnection> conn(
new NetworkConnection("example.com", 8080),
[](NetworkConnection* nc) {
if (nc) {
std::cout << "DEBUG: Custom deleter for NetworkConnection called." << std::endl;
nc->close(); // 调用资源的特定关闭方法
delete nc; // 然后释放内存
}
}
);
if (conn) {
conn->send("Hello, server!");
}
std::cout << "INFO: conn shared_ptr scope ending." << std::endl;
// 当conn的引用计数归零时,lambda删除器会被调用
}
// 另一个shared_ptr例子:管理std::ofstream
void demo_shared_ptr_ofstream() {
std::shared_ptr<std::ofstream> outFile(
new std::ofstream("output.txt"),
[](std::ofstream* os) {
if (os && os->is_open()) {
std::cout << "DEBUG: Custom deleter closing std::ofstream." << std::endl;
os->close();
delete os;
} else if (os) {
delete os;
}
}
);
if (outFile && outFile->is_open()) {
*outFile << "Writing something to output.txt\n";
}
std::cout << "INFO: outFile shared_ptr scope ending." << std::endl;
}
int main() {
demo_unique_ptr_functor();
std::cout << "------------------------\n";
demo_unique_ptr_lambda_thread();
std::cout << "------------------------\n";
demo_unique_ptr_func_ptr();
std::cout << "------------------------\n";
demo_shared_ptr_custom_resource();
std::cout << "------------------------\n";
demo_shared_ptr_ofstream();
return 0;
}可以看到,自定义删除器极大地扩展了智能指针的能力,让它们能够成为管理各种类型资源的通用工具,而不仅仅是内存。
说实话,刚接触智能指针时,很多人(包括我)可能只把它当成
new
delete
delete
fclose
closesocket
unlock
free
delete
自定义删除器就是为了解决这些痛点而生的。它把资源获取(Resource Acquisition)和资源释放(Resource Release)紧密地绑定在一起,完美地体现了RAII(Resource Acquisition Is Initialization)原则。当我拿到一个文件句柄,我立刻把它包装进一个带有
fclose
unique_ptr
unique_ptr
在选择自定义删除器的实现方式时,我通常会根据实际需求来权衡。这三种方式各有千秋,没有绝对的“最佳”,只有“最适合”。
函数对象(Functor): 这是最灵活的一种方式,因为它本质上是一个类。你可以让它携带状态,比如在删除器中记录一些日志信息,或者根据内部状态决定不同的清理策略。如果你的清理逻辑比较复杂,或者希望这个删除器可以在多个地方复用,并且需要一些配置参数,那么函数对象是首选。它的缺点是需要额外定义一个
struct
class
std::unique_ptr
unique_ptr
Lambda 表达式: 这是我个人最常用的一种方式,尤其是在C++11及更高版本中。Lambda表达式的优势在于简洁和局部性。你可以在需要的地方直接定义清理逻辑,并且可以轻松捕获当前作用域的变量,这使得它非常适合处理那些与特定上下文相关的资源清理。例如,一个删除器可能需要访问某个日志对象来记录关闭信息,或者需要一个ID来标识正在关闭的资源。Lambda的缺点嘛,对于
std::unique_ptr
decltype
std::shared_ptr
shared_ptr
函数指针: 这是最传统、最简单的方式,适用于那些无状态、通用的清理函数。比如,如果你需要用
free()
malloc()
&free
总结一下,如果清理逻辑简单且无状态,函数指针或简单lambda就够了。如果需要捕获上下文或处理复杂状态,lambda通常是我的首选。而如果删除器本身需要高度复用、配置或者承载复杂逻辑,那么函数对象无疑是更强大的工具。
自定义删除器虽然强大,但用不好也容易踩坑。我在实际开发中就遇到过一些问题,总结下来,有些陷阱是需要特别留意的,同时也有一些实践能让代码更健壮。
常见陷阱:
nullptr
nullptr
release()
nullptr
nullptr
以上就是C++智能指针自定义删除器 资源清理回调的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号