【问题标题】:std::unique_ptr with custom deleter for win32 LocalFreestd::unique_ptr 带有用于 win32 LocalFree 的自定义删除器
【发布时间】:2012-04-11 04:39:42
【问题描述】:

我有 win32 API CommandLineToArgvW,它返回一个 LPWSTR* 和 警告我

CommandLineToArgvW 分配一块连续的内存用于 指向参数字符串的指针,以及参数字符串的指针 他们自己;调用应用程序必须释放由 不再需要时的参数列表。要释放内存,请使用 对LocalFree 函数的一次调用。

http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx

在上述情况下释放内存的 C++ 惯用方法是什么?

我在想一个带有自定义删除器的std::unique_ptr,类似这样:

#include <Windows.h>
#include <memory>
#include <iostream>

template< class T >
struct Local_Del
{
   void operator()(T*p){::LocalFree(p);}
};

int main(int argc, char* argv[])
{
   {
      int n = 0;
      std::unique_ptr< LPWSTR, Local_Del< LPWSTR > > p( ::CommandLineToArgvW(L"cmd.exe p1 p2 p3",&n) );
      for ( int i = 0; i < n; i++ ) {
         std::wcout << p.get()[i] << L"\n";
      }
   }

    return 0;
}

上面的代码有问题吗?

【问题讨论】:

    标签: c++ winapi memory-management c++11 unique-ptr


    【解决方案1】:

    在我看来是正确的。您可以通过内联指定 unique_ptr 的删除器而不是为其创建函子来使其更简洁。

    std::unique_ptr<LPWSTR, HLOCAL(__stdcall *)(HLOCAL)> 
          p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), ::LocalFree );
    

    或者,如果您不想弄乱LocalFree 的签名和调用约定,您可以使用 lambda 进行删除。

    std::unique_ptr<LPWSTR, void(*)(LPWSTR *)> 
          p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), 
             [](LPWSTR *ptr){ ::LocalFree( ptr ); } );
    

    注意:在首次编写此答案时,VS2010 是可用的已发布 VS 版本。 doesn't support 将无捕获 lambda 转换为函数指针,因此您必须在第二个示例中使用 std::function

    std::unique_ptr<LPWSTR, std::function<void(LPWSTR *)>> 
          p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), 
             [](LPWSTR *ptr){ ::LocalFree( ptr ); } );
    

    【讨论】:

    • 我认为最后一个示例不需要std::function:无状态 lambda 可以转换为函数指针。 IE。 std::unique_ptr&lt;LPWSTR, void(*)(LPWSTR)&gt;p (...)
    • @MSalters 我确实尝试过,但在 VC10 和 g++4.6.2 下编译失败。前者的错误信息是error C2664: 'std::unique_ptr&lt;_Ty,_Dx&gt;::unique_ptr(wchar_t *,void (__stdcall *const &amp;)(LPWSTR *))' : cannot convert parameter 2 from 'anonymous-namespace'::&lt;lambda0&gt;' to 'void (__stdcall *const &amp;)(LPWSTR *)'
    • @MSalters 你是对的,无捕获的 lambda 可以转换为函数指针,所以 std::function 不是必需的。但是,VC10does not implement this。不知道我第一次尝试使用 g++ 时是如何搞砸的,但它确实有效。
    • 我想你可能已经被另一个问题搞糊涂了。我找到了LPWSTR * 嫌疑人。 LP 是指针类型定义的 Microsoft 前缀;大概您不想释放指向指针的指针。
    • "如果你不想弄乱 LocalFree 的签名和调用约定,你可以使用 lambda" - 或者直接使用 LocalFree() 作为删除器,然后使用decltype(LocalFree) 签名。
    【解决方案2】:

    自定义删除器的声明不是那么漂亮,使用decltype() 更快。 std::shared_ptr 是一个替代方案,但它大于std::unique_ptr。如果您不想共享指针,请使用unique_ptr

    std::unique_ptr<LPWSTR, decltype(::LocalFree)> 
         p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), ::LocalFree );
    

    【讨论】:

    • 在 Visual Studio 2010 上,它必须是 std::unique_ptr&lt;LPWSTR, decltype(&amp;::LocalFree)&gt;,否则构建将失败并出现神秘错误。那是因为decltype(::LocalFree)不是函数指针类型,而是函数类型。
    【解决方案3】:

    我发现shared_ptr 作为通用资源保护器更有用。它不需要删除器是模板参数的一部分,因此可以轻松传递。

    std::shared_ptr<LPWSTR> p(
        ::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n),
        ::LocalFree);
    

    【讨论】:

    【解决方案4】:

    那么使用Microsoft Windows Implementation Libraries (WIL) 的答案呢?

    首先“安装”WIL(从终端):

    c:\dev>git clone https://github.com/microsoft/wil.git
    

    然后:

    #include <Windows.h>
    #include <iostream>
    
    #include "c:/dev/wil/include/wil/resource.h"
    
    int main(int argc, char* argv[])
    {
       {
          int n = 0;
          wil::unique_hlocal_ptr<LPWSTR> p(::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n));
          for (int i = 0; i < n; i++) {
             std::wcout << p.get()[i] << L"\n";
          }
       }
    
       return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-31
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      • 2018-01-31
      相关资源
      最近更新 更多