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 技术?一个想法是使用指向 a 的智能指针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);}};
}