win32 LocalFree 的自定义删除器与 std::unique_ptr

18
我有一个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;
}

在上面的代码中有任何问题吗?
4个回答

12

我认为这是正确的。你可以通过内联指定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是可以使用的最新版本。它不支持将无捕获lambda转换为函数指针,因此您必须在第二个示例中使用std::function

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

1
对于最后一个示例,我认为您不需要使用std::function:无状态lambda可以转换为函数指针。即std::unique_ptr<LPWSTR,void(*)(LPWSTR)>p (...) - MSalters
@MSalters 我确实尝试过,但在VC10和g++4.6.2下编译失败。前者的错误信息是error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(wchar_t *,void (__stdcall *const &)(LPWSTR *))' : cannot convert parameter 2 from 'anonymous-namespace'::<lambda0>' to 'void (__stdcall *const &)(LPWSTR *)' - Praetorian
我认为你可能被另一个问题所困扰了。我觉得 LPWSTR * 有些可疑。LP 是 Microsoft 指针 typedefs 的前缀;很可能你并不想释放指向指针的指针。 - MSalters
1
如果您不想破坏LocalFree的签名和调用约定,可以使用lambda表达式,或者直接使用LocalFree()作为删除器,并使用decltype(LocalFree)作为签名。 - Remy Lebeau
+1 是因为你是唯一一个包含 VS2010 解决方法语法的人 - 我一早上都卡在这里了,其他人甚至没有提到 std::function。 - Bear
显示剩余2条评论

7

声明自定义删除器并不优雅,使用 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 );

4
在Visual Studio 2010中,必须使用std::unique_ptr<LPWSTR, decltype(&::LocalFree)>,否则会因为错误难以理解而构建失败。这是因为decltype(::LocalFree)不是函数指针类型,而是函数类型。 - Joker_vD

5

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

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

1
谢谢您的回答。我不太确定我是否更喜欢易于语法而不是正确的语义。在我看来,shared_ptr 在我的情况下并不适用。请参见http://www2.research.att.com/~bs/C++0xFAQ.html#std-shared_ptr与http://www2.research.att.com/~bs/C++0xFAQ.html#std-unique_ptr。 - Alessandro Jacopson
是的,你可能不想在程序中频繁传递指针,但了解相关技巧可能会有所帮助。 - Peter Vrabel

0

那么使用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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接