STL Container和ATL智能包裹类的冲突

STL Container和ATL智能包裹类的冲突

载自:http://www.codesky.net/article/200504/63245.html

 

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 files\microsoft visual studio\vc98\include\list(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 files\microsoft visual studio\vc98\include\list(235) : while compiling class-template member function 'class std::list<class ATL::CComBSTR,class std::allocator<class ATL::CComBSTR> >::iterator __thiscall std::list<class ATL::CComBSTR,class std::allocator<class ATL::CComBSTR> >::erase(class std::list<class ATL::CComBSTR,class std::allocator<class ATL::CComBSTR> >::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 T>

class CAdapt

{

public:

    。。。

       operator T&()

       {

              return m_T;

       }

       operator const T&() const

       {

              return m_T;

       }

       T m_T;

};

CAdapt模板的历史意义就在于此。

这样我们就可以放心大胆地声明道:

std::vector< CAdapt <CComBSTR> > vect;
typedef vector< CAdapt< CComPtr< IWhatever > > > TWhateverVector;

不再会有麻烦。

总结:

下面的声明都不会有编译错误:

std::vector<CComBSTR > vecBSTR;

std::vector< CComPtr<IPDH> > vecPDH0;

只有当容器是std::list时,才会发生编译错误。

 

而且,实际上使用下面的代码似乎也没有出现问题,比如内存泄漏等:

std::vector<CComBSTR > vec;

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

 

对于这种情况,Igor Tandetnik是这么说的:

“有时候,你在STL Container中没有用CAdapt,看上去平安无事。但是,这样的话,你的代码就和STL厂商的具体实现密切相关了。从而当你调用以前从没有用过的容器的某一个方法时,可能会发生一些未定义的事情。所以,Just to be on the safe side,无论何时,当你要把CComBSTR或者其他重载了operator&的类放入任何STL容器中时,请使用CAdapt。”

 

 

 

 

 

本文档所包含的信息代表了在发布之日,ZhengYun 对所讨论问题的当前看法,Zhengyun 不保证所给信息在发布之日以后的准确性。

本文档仅供参考。对本文档中的信息,Zhengyun 不做任何明示或默示的保证。

转载于:https://www.cnblogs.com/vedgtar/p/4978257.html

在MFC中实现 EventSink 。 (1) 在MFC中,添加ATL简单对象 CFileMonitorSink (2) 添加继承父 IDispEventImpl public IDispEventImpl (1) 0 唯一标识符, 用于区别 连接到 事件源的多个客户端 CFileMonitorSink, 当前名 _IFun1Events, COM 中的事件源接口, 包含各种事件 __ATLEventLib, COM 中Lib 具体查 MSDN --IDispEventImpl (2) 添加映射项 BEGIN_SINK_MAP(CFileMonitorSink) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 1, OnNotify) //0 唯一标识符,用于区别 连接到 事件源的多个客户端 同上 , 1, 事件号 , 发生1号事件 由OnNotify来处理 SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 2, OnNotify2) //发生2号事件 由OnNotify2来处理 END_SINK_MAP() 并添加方法 STDMETHOD(OnNotify)(void); //事件处理 STDMETHOD(OnNotify2)(CHAR* lszContent); (3) 连接到COM中的事件容器 添加变量 CComPtr m_Object; //COM 中的事件源对象 添加方法 STDMETHOD(Start)(IUnknown* pSinkThisObject, VARIANT_BOOL* succeeded) { AFX_MANAGE_STATE(AfxGetAppModuleState()); // TODO: 在此添加实现代码 if ( DispEventAdvise(pSinkThisObject) == S_OK ) { m_Object = pSinkThisObject; *succeeded = VARIANT_TRUE; } else { *succeeded = VARIANT_FALSE; } return S_OK; } STDMETHOD(Stop)(void) //解除连接 { AFX_MANAGE_STATE(AfxGetAppModuleState()); DispEventUnadvise(m_Object); return S_OK; } 在其他中的 使用方法: CComPtr m_FileMonitorSink; CComPtr m_FileMonitor; //COM中导出接口 CoInitialize(0); HRESULT lRt = m_FileMonitorSink.CoCreateInstance( __uuidof(FileMonitorSink) ); lRt = m_FileMonitor.CoCreateInstance(__uuidof(Fun1)); //创建COM接口实例 VARIANT_BOOL succeeded; lRt = m_FileMonitorSink->Start(m_FileMonitor, &succeeded); //把 m_FileMonitorSink 连接到COM中的事件容器上 m_FileMonitor->HelloWorld(); //调用COM接口,接口中触发事件s m_FileMonitorSink->stop(); //从COM接口中解除连接 CoUninitialize(); // ################# CFileMonitorSink 代码 ################# class ATL_NO_VTABLE CFileMonitorSink : public CComObjectRootEx, public CComCoClass, public IDispatchImpl, public IDispEventImpl { public: CFileMonitorSink() { } DECLARE_REGISTRY_RESOURCEID(IDR_FILEMONITORSINK) BEGIN_COM_MAP(CFileMonitorSink) COM_INTERFACE_ENTRY(IFileMonitorSink) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() BEGIN_SINK_MAP(CFileMonitorSink) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 1, OnNotify) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 2, OnNotify2) END_SINK_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } CComPtr m_Object; //COM 事件源对象 public: STDMETHOD(OnNotify)(void); STDMETHOD(Stop)(void); STDMETHOD(Start)(IUnknown* pSinkThisObject, VARIANT_BOOL* succeeded); STDMETHOD(OnNotify2)(CHAR* lszContent); };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值