Windows 句柄有时很烦人,需要记得在之后进行清理(使用创建的笔和画笔执行 GDI 就是一个很好的例子)。 RAII 解决方案很棒,但是为每种不同类型的手柄制作一个完整的(五规则)RAII 类真的那么好吗?当然不是!我能看到的最好的结果是一个完整的通用 RAII 类,其中其他类仅定义应清理句柄时要执行的操作,以及其他特定于句柄的方面。
例如,一个非常简单的模块类可以这样定义(只是一个例子):
struct Module {
Module() : handle_{nullptr} {}
Module(HMODULE hm) : handle_{hm, [](HMODULE h){FreeLibrary(h);}} {}
operator HMODULE() const {return handle_.get();}
private:
Handle<HMODULE> handle_;
};
这一切都很好,不需要析构函数或任何东西。当然,尽管如此,能够编写Handle
不需要析构函数或任何东西的类也很好。为什么不使用现有的 RAII 技术?一个想法是使用一个智能指针来void
,但这行不通。以下是正常情况下句柄的实际声明方式:
#define DECLARE_HANDLE(n) typedef struct n##__{int i;}*n
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HBITMAP);
DECLARE_HANDLE(HBRUSH);
...
它实际上区分了句柄类型,这很好,但它使得使用智能指针void
不可能的。相反,如果根据定义,句柄是指针,那么可以提取类型怎么办?
我的问题是以下假设是否是一个安全的假设。它使用桌面的句柄,必须将其关闭。除非共享指针和唯一指针之间存在差异(例如,FreeLibrary
有它自己的引用计数语义),假设句柄是一个指针并制作一个智能指针来指向它所指向的任何内容,或者我不应该使用智能指针并制作Handle
实施 RAII 方面本身?
#include <memory>
#include <type_traits>
#include <utility>
#include <windows.h>
int main() {
using underlying_type = std::common_type<decltype(*std::declval<HDESK>())>::type;
std::shared_ptr<underlying_type> ptr{nullptr, [](HDESK desk){CloseDesktop(desk);}};
}