是依靠一个Windows手柄的types是一个指针好吗?

Windows的句柄有时很烦人,记得清理后(使用创build的笔和刷子做GDI就是一个很好的例子)。 RAII解决scheme非常棒,但是为每个不同types的手柄制作一个完整的(规则五)RAII类真的很棒吗? 当然不是! 我能看到的最好的是一个完整的genericsRAII类,其他类只是定义了在清理句柄时要做什么以及其他句柄特定的方面。

例如,一个非常简单的模块类可以这样定义(只是一个例子):

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); ... 

它实际上区分句柄types,这是好的,但它使使用一个智能指针不可能void 。 如果相反,由于句柄是定义,指针,types可以被提取?

我的问题是,以下是否是一个安全的假设。 它使用桌面的句柄,必须closures。 除了共享指针和唯一指针之间的区别(例如, 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);}}; } 

Solutions Collecting From Web of "是依靠一个Windows手柄的types是一个指针好吗?"

我相信所有Windows指针在技术上都是指向系统的Windows内核部分内部对象的指针(有时可能指向由内核代码分配的用户端对象,或者该主题的一些变体)。

我不相信你应该把他们当作指针来对待。 它们只是纯粹技术角度的指针。 它们不比C风格更“指针”“FILE *”是一个指针。 我不认为你会建议使用shared_ptr<FILE*>来处理稍后的关闭文件。

将手柄包装成稍后清理的东西绝对是一个好主意,但我不认为使用智能指针解决方案是正确的解决方案。 使用知道如何关闭手柄的模板系统将是理想的。

我想你也需要处理“我想把这个句柄从这里传递到别的地方”,以一种适合所有人的好方式来处理 – 例如你有一个以某种方式获取资源的函数,并且返回那些句柄资源 – 你是否返回一个已经包装好的对象,如果是的话,这个拷贝是如何工作的?

如果在使用另一个句柄之前需要保存一个句柄的副本(例如保存当前的笔,然后设置一个自定义的那个,然后恢复)呢?

你可以采取的一种方法是使用模板类:

 template<typename H, BOOL(WINAPI *Releaser)(H)> class Handle { private: H m_handle; public: Handle(H handle) : m_handle(handle) { } ~Handle() { (*Releaser)(m_handle); } }; typedef Handle<HANDLE,&::CloseHandle> RAIIHANDLE; typedef Handle<HMODULE,&::FreeLibrary> RAIIHMODULE; typedef Handle<HDESK,&::CloseDesktop> RAIIHDESKTOP; 

如果有一个不是由BOOL(WINAPI)(HANDLE)类型的函数释放的BOOL(WINAPI)(HANDLE) ,那么你可能会遇到这个问题。 如果释放函数只是返回类型不同,可以添加作为模板参数,仍然使用此解决方案。

从技术上讲,这应该在所有当前版本的Windows下都能很好地工作,而且很难找到一个真正的原因(它实际上是对现有标准库功能的一个非常聪明的使用!),但是我仍然不喜欢这个想法是因为:

  1. 句柄是指针, 句柄不是指针 。 把手是不透明的类型,应该把它们当作兼容性来对待。 句柄是指针,句柄不太可能是不同的东西,但这可能会改变。 而且,句柄可能有也可能不具有有效的指针值,并且在32位和64位(如INVALID_HANDLE_VALUE )下可能有也可能没有不同的值,或者现在可能无法预见的其他副作用或行为。 假设一个句柄具有某些特定属性可能工作很好几十年,但它可能(理论上)神秘地失败,在某种情况下,你没有想到。 诚然,这是不太可能发生,但仍然不是100%干净。
  2. 以这种方式使用智能指针并不遵循最小的原则 。 因为,嘿, 句柄不是指针 。 将RAII内置到以直观名称命名的类中(“Handle”,“AutoHandle”)不会引起任何人扬起眉毛。

我相信unique_ptr和shared_ptr允许您提供自定义删除。 我相信这正是你需要正确管理句柄的生命周期。