如何正确地将_bstr_t重置为NULL(How to properly reset _bstr_t to `NULL`)

在摘录波纹管(简化的循环场景)

_bstr_t original(OLESTR("MyString")); // ref-count = 1 _bstr_t another; another = original; // ref-count = 2 // do something with another another.Assign(NULL); // expected: ref-count = 1, and another = NULL // reset another to NULL before doing other operations

我期待的another.Assign(NULL)是:

SysFreeString()不被调用 another设置为NULL ref-count递减为1 original BSTR内容的ref count=1 。

发生了什么:

SysFreeString()调用another和original底层BSTR another设置为NULL original遗骸的计数2

another.Assign(NULL)似乎为original和another解除了底层BSTR分配。 我们有意外的崩溃,因为在编码过程中我认为_bstr_t::Assign()会减少ref的数量,而不是直接释放BSTR 。

如何正确地将another重置为NULL而不影响original ?

请查看从VC++ 6 Assign实现。

// assignment operator copies internal data and increases reference count inline _bstr_t& _bstr_t::operator=(const _bstr_t& s) throw() { const_cast<_bstr_t*>(&s)->_AddRef(); _Free(); m_Data = s.m_Data; return *this; } // but _bstr_t::Assign() calls _bstr_t::Data_t::Assign() // without touching ref count inline void _bstr_t::Assign(BSTR s) throw(_com_error) { if (m_Data != NULL) { m_Data->Assign(s); } else { m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } } // it calls _bstr_t::Data_t::_Free() instead of _bstr_t::_Free() ! inline void _bstr_t::Data_t::Assign(BSTR s) throw(_com_error) { _Free(); if (s != NULL) { m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(s), ::SysStringByteLen(s)); } } // this _Free() straight away deallocates the BSTR! inline void _bstr_t::Data_t::_Free() throw() { if (m_wstr != NULL) { ::SysFreeString(m_wstr); } if (m_str != NULL) { delete [] m_str; } } // properly decrements ref count inline void _bstr_t::_Free() throw() { if (m_Data != NULL) { m_Data->Release(); m_Data = NULL; } }

In the snippet bellow (simplified scenario of a loop)

_bstr_t original(OLESTR("MyString")); // ref-count = 1 _bstr_t another; another = original; // ref-count = 2 // do something with another another.Assign(NULL); // expected: ref-count = 1, and another = NULL // reset another to NULL before doing other operations

What I expect after another.Assign(NULL) is:

SysFreeString() is NOT called another is set to NULL ref-count is decremented to 1 original has ref count=1 with existing BSTR content.

What happened:

SysFreeString() is called for underlying BSTR of both another and original another is set to NULL ref-count of original remains 2

another.Assign(NULL) seems to deallocate the underlying BSTR for both original and another. We had unexpected crash because during coding I thought _bstr_t::Assign() will decrement the ref count instead of straight away deallocating the BSTR.

How do I properly reset another to NULL without affecting original?

Please find bellow implementation of Assign from VC++ 6.

// assignment operator copies internal data and increases reference count inline _bstr_t& _bstr_t::operator=(const _bstr_t& s) throw() { const_cast<_bstr_t*>(&s)->_AddRef(); _Free(); m_Data = s.m_Data; return *this; } // but _bstr_t::Assign() calls _bstr_t::Data_t::Assign() // without touching ref count inline void _bstr_t::Assign(BSTR s) throw(_com_error) { if (m_Data != NULL) { m_Data->Assign(s); } else { m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } } // it calls _bstr_t::Data_t::_Free() instead of _bstr_t::_Free() ! inline void _bstr_t::Data_t::Assign(BSTR s) throw(_com_error) { _Free(); if (s != NULL) { m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(s), ::SysStringByteLen(s)); } } // this _Free() straight away deallocates the BSTR! inline void _bstr_t::Data_t::_Free() throw() { if (m_wstr != NULL) { ::SysFreeString(m_wstr); } if (m_str != NULL) { delete [] m_str; } } // properly decrements ref count inline void _bstr_t::_Free() throw() { if (m_Data != NULL) { m_Data->Release(); m_Data = NULL; } }

最满意答案

_bstr_t::Assign()的实现已经如Igor Tandetnik在他的评论中提到的那样进行了更新。

这是VS2010中的实现,它按预期工作:

inline void _bstr_t::Assign(BSTR s) { _COM_ASSERT(s == NULL || m_Data == NULL || m_Data->GetWString() != s); if (s == NULL || m_Data == NULL || m_Data->GetWString() != s) { _Free(); m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } }

The implementation of _bstr_t::Assign() has been updated as mentioned by Igor Tandetnik in his comment.

Here's the implementation in VS2010 and it works as expected:

inline void _bstr_t::Assign(BSTR s) { _COM_ASSERT(s == NULL || m_Data == NULL || m_Data->GetWString() != s); if (s == NULL || m_Data == NULL || m_Data->GetWString() != s) { _Free(); m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } }如何正确地将_bstr_t重置为NULL(How to properly reset _bstr_t to `NULL`)

在摘录波纹管(简化的循环场景)

_bstr_t original(OLESTR("MyString")); // ref-count = 1 _bstr_t another; another = original; // ref-count = 2 // do something with another another.Assign(NULL); // expected: ref-count = 1, and another = NULL // reset another to NULL before doing other operations

我期待的another.Assign(NULL)是:

SysFreeString()不被调用 another设置为NULL ref-count递减为1 original BSTR内容的ref count=1 。

发生了什么:

SysFreeString()调用another和original底层BSTR another设置为NULL original遗骸的计数2

another.Assign(NULL)似乎为original和another解除了底层BSTR分配。 我们有意外的崩溃,因为在编码过程中我认为_bstr_t::Assign()会减少ref的数量,而不是直接释放BSTR 。

如何正确地将another重置为NULL而不影响original ?

请查看从VC++ 6 Assign实现。

// assignment operator copies internal data and increases reference count inline _bstr_t& _bstr_t::operator=(const _bstr_t& s) throw() { const_cast<_bstr_t*>(&s)->_AddRef(); _Free(); m_Data = s.m_Data; return *this; } // but _bstr_t::Assign() calls _bstr_t::Data_t::Assign() // without touching ref count inline void _bstr_t::Assign(BSTR s) throw(_com_error) { if (m_Data != NULL) { m_Data->Assign(s); } else { m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } } // it calls _bstr_t::Data_t::_Free() instead of _bstr_t::_Free() ! inline void _bstr_t::Data_t::Assign(BSTR s) throw(_com_error) { _Free(); if (s != NULL) { m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(s), ::SysStringByteLen(s)); } } // this _Free() straight away deallocates the BSTR! inline void _bstr_t::Data_t::_Free() throw() { if (m_wstr != NULL) { ::SysFreeString(m_wstr); } if (m_str != NULL) { delete [] m_str; } } // properly decrements ref count inline void _bstr_t::_Free() throw() { if (m_Data != NULL) { m_Data->Release(); m_Data = NULL; } }

In the snippet bellow (simplified scenario of a loop)

_bstr_t original(OLESTR("MyString")); // ref-count = 1 _bstr_t another; another = original; // ref-count = 2 // do something with another another.Assign(NULL); // expected: ref-count = 1, and another = NULL // reset another to NULL before doing other operations

What I expect after another.Assign(NULL) is:

SysFreeString() is NOT called another is set to NULL ref-count is decremented to 1 original has ref count=1 with existing BSTR content.

What happened:

SysFreeString() is called for underlying BSTR of both another and original another is set to NULL ref-count of original remains 2

another.Assign(NULL) seems to deallocate the underlying BSTR for both original and another. We had unexpected crash because during coding I thought _bstr_t::Assign() will decrement the ref count instead of straight away deallocating the BSTR.

How do I properly reset another to NULL without affecting original?

Please find bellow implementation of Assign from VC++ 6.

// assignment operator copies internal data and increases reference count inline _bstr_t& _bstr_t::operator=(const _bstr_t& s) throw() { const_cast<_bstr_t*>(&s)->_AddRef(); _Free(); m_Data = s.m_Data; return *this; } // but _bstr_t::Assign() calls _bstr_t::Data_t::Assign() // without touching ref count inline void _bstr_t::Assign(BSTR s) throw(_com_error) { if (m_Data != NULL) { m_Data->Assign(s); } else { m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } } // it calls _bstr_t::Data_t::_Free() instead of _bstr_t::_Free() ! inline void _bstr_t::Data_t::Assign(BSTR s) throw(_com_error) { _Free(); if (s != NULL) { m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(s), ::SysStringByteLen(s)); } } // this _Free() straight away deallocates the BSTR! inline void _bstr_t::Data_t::_Free() throw() { if (m_wstr != NULL) { ::SysFreeString(m_wstr); } if (m_str != NULL) { delete [] m_str; } } // properly decrements ref count inline void _bstr_t::_Free() throw() { if (m_Data != NULL) { m_Data->Release(); m_Data = NULL; } }

最满意答案

_bstr_t::Assign()的实现已经如Igor Tandetnik在他的评论中提到的那样进行了更新。

这是VS2010中的实现,它按预期工作:

inline void _bstr_t::Assign(BSTR s) { _COM_ASSERT(s == NULL || m_Data == NULL || m_Data->GetWString() != s); if (s == NULL || m_Data == NULL || m_Data->GetWString() != s) { _Free(); m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } }

The implementation of _bstr_t::Assign() has been updated as mentioned by Igor Tandetnik in his comment.

Here's the implementation in VS2010 and it works as expected:

inline void _bstr_t::Assign(BSTR s) { _COM_ASSERT(s == NULL || m_Data == NULL || m_Data->GetWString() != s); if (s == NULL || m_Data == NULL || m_Data->GetWString() != s) { _Free(); m_Data = new Data_t(s, TRUE); if (m_Data == NULL) { _com_issue_error(E_OUTOFMEMORY); } } }