12

我最近在SO发布了一个关于 RAII 的一般性问题。但是,我的 HANDLE 示例仍然存在一些实现问题。

AHANDLE被输入到void *in 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:最后一个参数是 a PHANDLE。演员阵容中等丑陋。

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);

示例 3 Process32First:第一个参数是 a HANDLE。真的很丑。

Process32First(*((PHANDLE)&h), &pEntry);

示例 4与常数的简单比较HANDLE。真的很丑。

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }

为 HANDLE 创建正确的 shared_ptr 的正确方法是什么?

4

4 回答 4

11

示例 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 类似的东西:

typedef shared_ptr<void> SharedHandle;

或者更好的是,如果要使用 CloseHandle() 关闭所有句柄,则创建一个 SharedHandle 类来包装 shared_ptr 并自动提供正确的删除器:

// 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;
};
于 2009-10-13T20:02:42.927 回答
3

不要为此烦恼 shared_ptr,使用 ATL::CHandle。

原因如下:

  • 当您看到时,CHandle您就知道它是手柄的 RAII 包装器。
  • 当你看到shared_ptr<void>你不知道它是什么。
  • CHandle doesn't make an ownership shared (however in some cases you may want a shared ownership).
  • CHandle is a standard for a windows development stack.
  • CHandle is more compact than shared_ptr<void> with custom deleter (less typing/reading).
于 2012-02-02T11:00:54.383 回答
2

看一下boost 2:shared_ptr 包装资源句柄

于 2009-10-13T20:21:07.990 回答
2

这是我的替代方案,它非常好,只是你需要总是在之后取消引用.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);
    delete 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.

当然,辅助函数适用于任何类型的句柄。

于 2012-02-02T01:41:26.460 回答