Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145792
  • 博文数量: 124
  • 博客积分: 70
  • 博客等级: 民兵
  • 技术积分: 1745
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-24 13:49
文章分类

全部博文(124)

文章存档

2011年(55)

2010年(14)

2009年(30)

2008年(25)

我的朋友

分类: WINDOWS

2010-08-19 11:22:41

http://hi.baidu.com/viecks/blog/item/f2ede2c4450a88a08326ac2a.html

STL ContainerATL智能包裹类的冲突

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 >::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的智能包裹类,如CComBSTrCCOMPtr等,却都重载了操作符&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 side,无论何时,当你要把CComBSTR或者其他重载了operator&的类放入任何STL容器中时,请使用CAdapt

 
 
 
 
template<typename T>
class
 CT
{

public
:
   
operator T&()
   {
      
return m_T;
   }

   T& 
operator& ()
   {
      
return m_T;
   }

private
:
   T m_T;
};
 
 
这儿的operator T&() 和T& operator& ()区别?
 
CT a;
 int ff = &a;   // call 2nd operator
 int& i = (int &)a;  // call 1st
 i =2;
 
也就是说第一个operator是类型转换操作符;
第二个是取地址转换操作符
 
 
auto_ptr crash
 
 
template
 Functor(PTR p) //: m_spHandlerImp(new FuncHandler(p))
 {
  //FuncHandler* pFunc = new FuncHandler(p); 
  //FuncHandlerImp* pImpl = static_cast(pFunc);
  //m_spHandlerImp = pImpl;
   m_spHandlerImp.reset(new FuncHandler(p));
 }
 
上述的代码,如果使用注释部分必然crash,后续调用.
 
 
 
有一天发现, ww的tips都实效了,差来查去后来发现.
atlctls.h
 
-               cbSize = sizeof(TOOLINFO);
+               cbSize = GetToolInfoCbSize();
 
即TOOLINFO这个宏是受WINNT控制的,而某人一不小心修改了此宏,而代码编译时候的sizeof 和用户执行时候的环境不一致导致了这个问题.
阅读(706) | 评论(0) | 转发(0) |
0

上一篇:逆向基础

下一篇:一步一步学习bind

给主人留下些什么吧!~~