std::unique_ptr自定义删除器能否用非指针参数?求C++11替代方案
问题描述
我最近尝试用std::unique_ptr实现通用RAII机制来管理文件描述符,示例代码如下:
// main.cpp #include <memory> #include <sys/fcntl.h> #include <unistd.h> #define RAII_CAT(x) raii ## x #define RAII_CAT2(x) RAII_CAT(x) #define RAII_FD(X) \ std::unique_ptr<int, void(*)(int*)> RAII_CAT2(__LINE__){X, [](int* x){ if (-1 != *x) { close(*x); }}} int main(int argc, char* argv[]) { { int fd = open("foo.txt", O_RDONLY); RAII_FD(&fd); } end: return 0; }
这里的问题是,std::unique_ptr的自定义删除器必须接收int*类型参数,但close()本身只需要int类型参数。我尝试把删除器改成接收int类型时,出现了int*/int类型不匹配的编译错误。
现在需要在C++11、非Boost环境下,找到一种通用RAII的替代方案,要求自定义删除器不需要接收指针参数,优先用原生STL的轻量实现。
方案1:自定义轻量通用RAII模板类
直接实现一个通用的RAII包装类,将资源和销毁函数绑定,完全摆脱unique_ptr的指针语义限制,删除器直接接收资源本身:
#include <functional> #include <sys/fcntl.h> #include <unistd.h> template<typename T> class RAIIWrapper { public: using Deleter = std::function<void(T)>; RAIIWrapper(T resource, Deleter deleter) : m_resource(resource), m_deleter(std::move(deleter)), m_valid(true) {} // 禁止拷贝 RAIIWrapper(const RAIIWrapper&) = delete; RAIIWrapper& operator=(const RAIIWrapper&) = delete; // 支持移动语义 RAIIWrapper(RAIIWrapper&& other) noexcept : m_resource(other.m_resource), m_deleter(std::move(other.m_deleter)), m_valid(other.m_valid) { other.m_valid = false; } RAIIWrapper& operator=(RAIIWrapper&& other) noexcept { if (this != &other) { if (m_valid) { m_deleter(m_resource); } m_resource = other.m_resource; m_deleter = std::move(other.m_deleter); m_valid = other.m_valid; other.m_valid = false; } return *this; } ~RAIIWrapper() { if (m_valid) { m_deleter(m_resource); } } // 获取原始资源 T get() const { return m_resource; } private: T m_resource; Deleter m_deleter; bool m_valid; // 标记资源有效性,避免重复销毁 }; // 文件描述符便捷宏 #define RAII_FD(FD) \ RAIIWrapper<int> RAII_CAT2(__LINE__)(FD, [](int fd) { if (fd != -1) close(fd); }) // 宏拼接逻辑 #define RAII_CAT(x) raii ## x #define RAII_CAT2(x) RAII_CAT(x) int main(int argc, char* argv[]) { { int fd = open("foo.txt", O_RDONLY); RAII_FD(fd); // 直接传入int类型的fd,无需指针 // 正常使用fd进行操作 } return 0; }
这个方案的优势:
- 删除器直接接收资源本身(如
int类型的fd),完全匹配close()的参数要求 - 模板类通用,可复用在socket、自定义句柄等其他资源管理场景
- 纯C++11 STL实现,无外部依赖,开销极小
方案2:基于std::unique_ptr的资源包装方案
如果一定要依赖std::unique_ptr,可以将资源包装为单元素结构体,让unique_ptr管理结构体指针,删除器通过结构体访问原始资源:
#include <memory> #include <sys/fcntl.h> #include <unistd.h> // 包装文件描述符的结构体 struct FDHolder { int fd; explicit FDHolder(int f) : fd(f) {} }; #define RAII_FD(FD) \ std::unique_ptr<FDHolder, void(*)(FDHolder*)> RAII_CAT2(__LINE__)( \ new FDHolder(FD), [](FDHolder* holder) { \ if (holder->fd != -1) close(holder->fd); \ delete holder; \ }) #define RAII_CAT(x) raii ## x #define RAII_CAT2(x) RAII_CAT(x) int main(int argc, char* argv[]) { { int fd = open("foo.txt", O_RDONLY); auto raii_fd = RAII_FD(fd); // 通过raii_fd->fd访问原始文件描述符 } return 0; }
该方案基于unique_ptr实现,但需要额外分配结构体内存,相比方案1稍重,适合必须使用unique_ptr的场景。
内容的提问来源于stack exchange,提问作者StoneThrow
相关产品推荐
相关产品推荐

