STL Container和ATL智慧包裹類的衝突 (轉)

worldblog發表於2007-12-13
STL Container和ATL智慧包裹類的衝突 (轉)[@more@]

STL Container和ATL智慧包裹類的衝突:namespace prefix = o ns = "urn:schemas--com::office" />

Article last modified on 2002-8-7

----------------------------------------------------------------

The information in this article applies to:

-  C/C++

-  Microsoft Visual C++ 6.0(SP5)

----------------------------------------------------------------

如果你在中這麼宣告:

std::list< CComBSTR > list;

那麼MSVC6.0(SP5)就會產生一個編譯錯誤:

f:program filesmicrosoft vc98includelist(238) : error C2664: 'destroy' : cannot convert parameter 1 from 'unsigned short ** ' to 'class ATL::CComBSTR *'

Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

f:program filesmicrosoft visual studiovc98includelist(235) : while compiling class-template member function 'class std::list >::iterator __thiscall std::list >::erase(class std::list >::iterator)'

錯誤定位於List頭的238行:

iterator erase(iterator _P)

  {_Nodeptr _S = (_P++)._Mynode();

  _Acc::_Next(_Acc::_Prev(_S)) = _Acc::_Next(_S);

  _Acc::_Prev(_Acc::_Next(_S)) = _Acc::_Prev(_S);

  allocator.destroy(&_Acc::_Value(_S));

  _Freenode(_S);

  --_Size;

  return (_P); }

原因:過載operator&的行為破壞了CopyConstructible

按照C++標準,可以在任何容器中的都必須有CopyConstructible。

CopyConstructible的一個要求就是,假設t是儲存在容器中的一個型別為T的物件,那麼&t就返回T*,也就是t的地址。

而ATL的智慧包裹類,如CComBSTr、CCOMPtr等,卻都過載了運算子&,CComBSTR類的&運算子返回CComBSTR::m_str,這是一個BSTR型別。這樣就破壞了CopyConstructible的要求。

說到這裡,我們就不難理解為什麼“error C2664: 'destroy' : cannot convert parameter 1 from 'unsigned short ** ' to 'class ATL::CComBSTR *'”了。

這種過載了&運算子的行為,在與STL聯合使用時,會導致各種各樣的問題(從洩漏到隨機崩潰)。

這些危險物件包括有:

CComPtr

CComQIPtr

CComBSTR

_com_ptr_t

千萬不要把它們放入任何STL容器中。當然也要留意其他過載了operator&的類。

解決辦法:使用CAdapt模板類

ATL為我們提供瞭解決辦法:CAdapt模板類。

這個模板過載了&運算子,不再讓它返回物件的地址:

//  AtlBase.h  Line 864

template

class CAdapt

{

public:

  。。。

  operator T&()

  {

  return m_T;

  }

  operator const T&() const

  {

  return m_T;

  }

  T m_T;

};

CAdapt模板的歷史意義就在於此。

這樣我們就可以放心大膽地宣告道:

std::vector< CAdapt > vect;


typedef vector< CAdapt< CComPtr< IWhatever > > > TWhateverVector;


不再會有麻煩。

總結:

下面的宣告都不會有編譯錯誤:

std::vector vecBSTR;

std::vector< CComPtr > vecPDH0;

只有當容器是std::list時,才會發生編譯錯誤。

而且,實際上使用下面的程式碼似乎也沒有出現問題,比如記憶體洩漏等:

std::vector vec;

vec.push_back(CComBSTR("string"));

對於這種情況,Igor Tandetnik是這麼說的:

“有時候,你在STL Container中沒有用CAdapt,看上去平安無事。但是,這樣的話,你的程式碼就和STL廠商的具體實現密切相關了。從而當你以前從沒有用過的容器的某一個方法時,可能會發生一些未定義的事情。所以,Just to be on the safe s,無論何時,當你要把CComBSTR或者其他過載了operator&的類放入任何STL容器中時,請使用CAdapt。”

 

 

 

 

本文件所包含的資訊代表了在釋出之日,ZhengYun 對所討論問題的當前看法,Zhengyun 不保證所給資訊在釋出之日以後的準確性。

本文件僅供參考。對本文件中的資訊,Zhengyun 不做任何明示或默示的保證。

Written by zhengyun@tomosoft.com


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992783/,如需轉載,請註明出處,否則將追究法律責任。

相關文章