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
,在其上调用 SysFreeString
? std::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:
考虑下面的代码,这里有内存泄漏吗?
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
,在其上调用 SysFreeString
? std::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: