【问题标题】:How to use C++ standard smart pointers with Windows HANDLEs?如何在 Windows HANDLE 中使用 C++ 标准智能指针?
【发布时间】:2019-08-01 01:37:34
【问题描述】:

我想知道是否有办法将unique_ptr<T> 与 Windows HANDLE 一起使用?

我正在考虑将std::default_delete 替换为调用CloseHandle 的特定handle_trats。问题是 HANDLE 定义为 void* unique_ptr<void> 不会编译,因为 sizeof(void) 未定义。

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

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

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

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

【问题讨论】:

    标签: c++ winapi c++11


    【解决方案1】:

    这个问题可以扩展到 COM IUnknown 指针 - 可以 CComPtr 被任何标准智能指针替换?

    是的。你没有专门化std::default_deleter,你只是替换删除器类型。

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

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

    【讨论】:

      【解决方案2】:

      创建一个特定的智能指针类,不会花很长时间。不要滥用图书馆课程。句柄语义与 C++ 指针的语义完全不同;一方面,取消引用 HANDLE 是没有意义的。

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

      【讨论】:

      • 我编写了自己设置的特定于 HANDLE 的类,因为一些句柄需要 CloseHandle() 而另一些则需要不同的 API 特定函数,一些 HANDLE 在未分配时设置为 INVALID_HANDLE_VALUE,其他设置为 NULL相反,等等。我为大部分工作编写了一组基类,然后使用自定义 trait 类来处理闭包和赋值问题。
      • -1 用于严重地重新实现库类。它们是为此功能明确设计的,这不是滥用。您将引入毫无意义的新错误和代码。
      • +1 用于识别指针和 HANDLE 是具有不同语义的不同事物。将 HANDLE 硬塞进智能指针类型会为您提供一堆对 HANDLE 没有真正意义的方法。
      【解决方案3】:

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

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

      适用于 MSVC 2010。请注意,您需要在 decltype() 中为函数名称指定“&”以推断指向函数的类型。

      【讨论】:

        【解决方案4】:

        您可以使用自定义删除器键入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(...));
        

        【讨论】:

          【解决方案5】:

          另一种解决方案

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

          【讨论】:

            【解决方案6】:

            你可以创建一个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);
            

            【讨论】:

              【解决方案7】:

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

              我建议您查看A Proposal to Add additional RAII Wrappers to the Standard Library 和the drawbacks of using smart pointers with handles。

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

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                  【解决方案9】:

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

                  【讨论】:

                    【解决方案10】:

                    这是我用来处理HANDLE 的一个方便的小“custom_ptr”。 需要C++17 才能编译。这个想法是从另一个 SO 帖子中提出来的,我只添加了 std::remove_pointer_t&lt;T&gt;,这样我就不必将 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" ) };
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-03-18
                      • 1970-01-01
                      • 2019-09-21
                      • 2011-08-12
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多