BSTR 内存泄漏到 wstring

snb*_*snb 1 c++ com memory-leaks bstr

考虑以下代码,这是否存在内存泄漏?

HRESULT GetPath(wstring &outDomainPath)
{
    CComBSTR bstrDomainPath;

    AnotherGetPath(&bstrDomainPath);
    outDomainPath = bstrDomainPath.Detach();
}
Run Code Online (Sandbox Code Playgroud)

这有什么区别?(还是内存泄漏??)

HRESULT GetPath(wstring &outDomainPath)
{
    CComBSTR bstrDomainPath;

    AnotherGetPath(&bstrDomainPath);
    outDomainPath = bstrDomainPath;//removed detach
}
Run Code Online (Sandbox Code Playgroud)

Mr.*_*C64 5

CComBSTR是围绕原始BSTR字符串的 RAII 包装器。

在您发布的第一个代码片段中:

outDomainPath = bstrDomainPath.Detach();
Run Code Online (Sandbox Code Playgroud)

您调用CComBSTR::Detach,它释放包装BSTR字符串的所有权,并将此所有权转移给“其他人”。
在您的情况下,“其他人”是一个std::wstring对象(由 引用outDomainPath),它不是围绕BSTRs的 RAII 包装器。

现在,考虑一下,从 C++ 类型系统的角度来看, aBSTR基本上是 a wchar_t*。此外,假设这些原始指针是 C 风格的 NUL 终止字符串,std::wstring实现const wchar_t*作为输入的构造函数和赋值运算符重载wchar_t。因此,您可以std::wstring从原始 C 样式字符串创建对象。

问题是,当你调用CComBSTR::Detach,原BSTR所有权转移给调用者,负责妥善释放BSTR字符串。

但是,在您的情况下,您正在wstringwchar_t*( BSTR) 返回的对象创建一个对象CComBSTR::Detach“孤立”返回的BSTR字符串。

事实上,谁将负责正确释放BSTR, 调用SysFreeString它?该std::wstring析构函数不会那么做的。

所以,在这种情况下,您泄漏了BSTR由返回CComBSTR::Detach


另一方面,在第二个代码片段中:

outDomainPath = bstrDomainPath;//removed detach
Run Code Online (Sandbox Code Playgroud)

发生的情况是编译器隐式调用CComBSTR::operator BSTR,这基本上使调用者可以访问BSTR包装在里面的CComBSTR. 请注意,在这种情况下,有没有所有权的转移:你只是观察到BSTR,这仍然是由拥有CComBSTRRAII包装。

std::wstring具有构造函数和 op= 重载以wstring从原始的const wchar_t*NUL 终止的 C 样式字符串创建对象。BSTR基本上是wchar_t*从 C++ 类型系统的角度来看,因此如果您BSTR包含以 NUL 结尾的字符串(内部没有嵌入的 NUL ),则该行代码将复制BSTR字符串,将其复制到一个std::wstring对象中。

然后,在您的GetPath函数结束时,本地CComBSTR bstrDomainPath对象将超出范围,CComBSTR析构函数将被 C++ 编译器自动调用,SysFreeString并将被调用以正确释放包装的BSTR.

在这种情况下,您没有BSTR泄漏。

请注意,此代码的工作,如果你已经在你的嵌入式完全无效BSTR。在嵌入NUL 的情况下,只会复制第一个“子字符串”,因为wstring将 aconst wchar_t*作为输入的构造函数将在第一个NUL处停止。

此外,我鼓励您阅读这篇有趣的博客文章:

Eric Lippert 的 BSTR 语义指南