我最近在SO发布了关于RAII的一般性问题。 但是,我的HANDLE示例仍然存在一些实现问题。
在HANDLE
中void *
的{{1}}类型设置为windows.h
。因此,正确的shared_ptr
定义需要
std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);
示例1 CreateToolhelp32Snapshot
:返回HANDLE
并继续工作。
const std::tr1::shared_ptr<void> h
(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);
当我在定义中使用void
时(正确的方法是什么?)问题继续存在,当我尝试用这个指针调用更多的winapi命令时。它们在功能上有效,但很难看,我确信必须有更好的解决方案。
在以下示例中,h
是通过顶部定义创建的指针。
示例2 OpenProcessToken
:最后一个参数是PHANDLE
。与演员一样丑陋。
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
(PHANDLE)&h);
示例3 Process32First
:第一个参数是HANDLE
。非常难看。
Process32First(*((PHANDLE)&h), &pEntry);
示例4 与常量HANDLE
的简单比较。非常难看。
if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }
为HANDLE创建正确的shared_ptr的正确方法是什么?
答案 0 :(得分:9)
示例1没问题
示例2是错误的。通过盲目地转换为PHANDLE,绕过shared_ptr逻辑。它应该是这样的:
HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);
或者,分配给预先存在的shared_ptr:
shared_ptr<void> safe_h = ....
{
HANDLE h;
OpenProcessToken(...., &h);
safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle
或者,创建一个自己的,安全的OpenProcessToken版本,它返回一个共享句柄而不是一个PHANDLE:
// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
HANDLE h = INVALID_HANDLE_VALUE;
::OpenProcessToken(...., &h);
return SharedHandle(h);
}
示例3:无需走这些弯路。这应该没问题:
Process32First(h.get(), ...);
示例4:再次,没有绕道而行:
if (h.get() == INVALID_HANDLE){...}
为了让事情变得更好,你可以输入类似的东西:
typedef shared_ptr<void> SharedHandle;
或者更好的是,如果要使用CloseHandle()关闭所有句柄,请创建一个包装shared_ptr的SharedHandle类并自动提供正确的删除器:
// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
HANDLE get()const{return m_Handle.get();}
//Expose other shared_ptr-like methods as needed
//...
private:
shared_ptr<void> m_Handle;
};
答案 1 :(得分:3)
不要为了这个而使用shared_ptr,请使用ATL :: CHandle。
原因如下:
CHandle
时,您知道它是句柄的RAII包装器。shared_ptr<void>
时,您不知道它是什么。CHandle
不会共享所有权(但在某些情况下,您可能需要共享所有权)。CHandle
是Windows开发堆栈的标准。CHandle
比使用自定义删除工具的shared_ptr<void>
更紧凑(更少打字/阅读)。答案 2 :(得分:2)
答案 3 :(得分:1)
这是我的替代方案,除非你需要在.get()
之后始终取消引用并且需要一个函子或lambda,这是非常好的:
template<typename HandleType, typename Deleter>
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx)
{
return std::shared_ptr<HandleType>(new HandleType(_handle), _dx);
}
然后:
auto closeHandleDeleter = [](HANDLE* h) { ::CloseHandle(*h); };
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter);
f_that_takes_handle(*sp.get());
我最喜欢这个是没有额外的工作可以访问这个:
std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs.
当然,辅助函数适用于任何类型的句柄类型。