如何在Windows HANDLEs中使用C++标准智能指针

How to use C++ standard smart pointers with Windows HANDLEs?

本文关键字:C++ 标准 智能 指针 Windows HANDLEs      更新时间:2023-10-16

我想知道是否有一种方法可以将unique_ptr<T>与Windows HANDLEs一起使用?

我想用调用CloseHandle的特定handle_trats替换std::default_delete。问题是HANDLE被定义为void* unique_ptr<void>不会编译,因为sizeof(void)没有被定义。

到目前为止,我只看到两种可能性:

  1. 为HANDLEs创建一个包装类,并像这样使用它:unique_ptr<new CHandle(h)>。这几乎使unique_ptr<T>本身变得毫无用处
  2. 使用类似于unique_ptr<T>的特定于HANDLE的智能指针类

你认为什么是更好的选择?你有什么建议?

这个问题可以扩展到COM IUnknown指针——CComPtr可以被任何标准智能指针取代吗?

这个问题可以扩展到COM IUnknown指针-can CComPtr被任何标准智能指针所取代?

是的。您不专门化std::default_deleter,只需替换deleter类型即可。

struct COMDeleter {
    template<typename T> void operator()(T* ptr) {
        ptr->Release();
    }
};
unique_ptr<IUnknown, COMDeleter> ptr; // Works fine

同样的原理适用于shared_ptr,实际上也适用于HANDLE

创建一个特定的智能指针类,不会花很长时间。不要滥用图书馆的课程。句柄语义与C++指针的句柄语义大不相同;首先,取消对HANDLE的引用毫无意义。

使用自定义智能句柄类的另一个原因是NULL并不总是意味着空句柄。有时它是INVALID_HANDLE_VALUE,这是不一样的(实际上是-1)。

受Alexander Drichel解决方案的启发,这里有更短的

std::unique_ptr< HANDLE, decltype(&CloseHandle) > uniqueHandle( nullptr, CloseHandle );

在MSVC 2010工作。请注意,您需要指定"&"用于decltype()中的函数名,以推导指向函数类型的指针。

您可以使用自定义的deleter 来typedef您的unique_ptr

struct handle_deleter
{
    void operator()(void* handle)
    {
        if(handle != nullptr)
            CloseHandle(handle);
    }
};
typedef std::unique_ptr<void, handle_deleter> UniqueHandle_t;
UniqueHandle_t ptr(CreateFile(...));

另一种解决方案

std::unique_ptr< void, void(*)( HANDLE ) > uniqueHandle( file, []( HANDLE h ) { ::CloseHandle( h ); } );

您可以创建一个Deleter类来释放句柄,而不是调用delete()。

您可以在LINK中看到他们如何解决使用shared_ptr删除数组的问题(unique_ptr还有一个接收Delete类的构造函数)

  struct handle_deleter
  {   
    void operator ()( HANDLE handle)
      { CloseHandle(p); }
  };
  HANDLE blah = GetSomeHandle();
  unique_ptr myPointer(blah,handle_deleter);

我不建议使用带句柄的智能指针。

我建议您查看向标准库添加额外RAII包装的建议,以及使用带句柄的智能指针的缺点。

就我个人而言,我正在使用该建议的参考实现,而不是在这些情况下使用std::unique_ptr

HANDLE并不总是用CloseHandle()关闭,请注意。例如,用FindNextFile()打开的HANDLE必须由FindClose()关闭。

必须使用CloseHandle()来"关闭"句柄,而不是使用delete。一旦它们没有在其他地方打开,Windows就会将其删除。因此,您可以编写一个调用CloseHandle()的包装器,而不是删除它。您还可以编写自己的智能指针类,这可能会更好,因为不再需要包装器了。

这里有一个我用来处理HANDLE的方便的小"custom_ptr"。需要C++17才能进行编译。这个想法是从另一篇SO帖子中提出的,我只添加了std::remove_pointer_t<T>,这样我就不必将void作为custom_ptr类型参数传递。

定义

#include <type_traits>
#include <memory>
template<auto fn>
using deleter_from_fn = std::integral_constant<decltype( fn ), fn>;
template<typename T, auto fn>
using custom_ptr = std::unique_ptr<typename std::remove_pointer_t<T>, deleter_from_fn<fn>>;

使用

custom_ptr<HANDLE, &CloseHandle> phandle{
    CreateFileMapping( INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 1024, L"FileName" ) };