BSTR 中的内存泄漏到 wstring

Memory leak in BSTR to wstring

考虑下面的代码,这里有内存泄漏吗?

HRESULT GetPath(wstring &outDomainPath)
{
    CComBSTR bstrDomainPath;

    AnotherGetPath(&bstrDomainPath);
    outDomainPath = bstrDomainPath.Detach();
}

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

HRESULT GetPath(wstring &outDomainPath)
{
    CComBSTR bstrDomainPath;

    AnotherGetPath(&bstrDomainPath);
    outDomainPath = bstrDomainPath;//removed detach
}

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

在您 post 编辑的第一个代码片段中:

outDomainPath = bstrDomainPath.Detach();

您调用 CComBSTR::Detach,它 释放包装的 BSTR 字符串的所有权 ,并且 转移 这个所有权给“其他人”。
在你的例子中,“其他人”是一个 std::wstring 对象(由 outDomainPath 引用),它是 而不是 BSTR 周围的 RAII 包装器。

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

问题是,当您调用 CComBSTR::Detach 时,原始 BSTR 所有权被转移给调用者,调用者负责正确地 释放 BSTR 字符串.

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

事实上,谁将负责正确释放 BSTR,在其上调用 SysFreeStringstd::wstring 析构函数不会那样做。

因此,在这种情况下,您泄漏了 CComBSTR::Detach 返回的 BSTR


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

outDomainPath = bstrDomainPath;//removed detach

编译器会隐式调用 CComBSTR::operator BSTR,这基本上让调用者可以访问包裹在 CComBSTR 中的 BSTR。请注意,在这种情况下,没有所有权转移:您只是观察BSTR,它仍然属于CComBSTR RAII 包装器。

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

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

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

请注意,如果您没有在您的BSTR中嵌入 NUL,则此代码有效。在嵌入 NUL 的情况下,只有第一个 "substring" 会被复制,因为接受 const wchar_t* 作为输入的 wstring 构造函数将停止在 第一个 NUL.

此外,我鼓励您阅读这个有趣的博客 post:

Eric Lippert's Guide to BSTR Semantics