COM组件粗谈-STL容器与COM智能指针

引言

工作项目中遇到过一个问题,代码中某个函数导致内存泄漏,而且泄漏随此函数调用次数变化而变,当调用次数较少时,泄漏并不明显,而当该函数调用次数超过千次后,内存泄漏就十分明显。
具体代码如下

HRESULT GetWhatYouWantInLocal(ULONG i_uIdx,IRoHaHa** o_ppComPosture)
 {
  std::vector<IZYouWantPtr> vecRoElem;
  GetYouWant(vecRoElem);
  ULONG uCnt = vecRoElem.size();
  if (i_uIdx >= uCnt || uCnt == 0)
  {
   return E_FAIL;
  }
 ……
   return S_OK;
 }

当时百思不得其解,找了领导一看,立马指出下面两句就是根源所在。

 std::vector<IZYouWantPtr> vecRoElem;
  GetYouWant(vecRoElem);

即STL容器不能与COM智能指针共用。

原因

原因:重载operator&的行为破坏了CopyConstructible
具体原因,经度娘和谷哥之后,有如下说法:

  • Effective STL中明确说了STL不支持智能指针,因为智能指针是不支持拷贝构造和可赋值的。
  • vector中元素的两个要求是:1.元素必须能赋值2.元素必须能复制 。
  • 按照C++标准,可以存储在任何容器中的对象都必须有CopyConstructible。
  • 从另外一个角度解释的:智能指针为了保证自己的智能性,没有定义一个参数为const auto_ptr& 的拷贝构造函数。但是vector在声明的时候,就要用这样的拷贝构造函数。
  • 忠告:不建议将对象作为容器的元素, 一般都是存放对象的指针, 而且不建议智能指针和容器结合使用。 因为智能指针有引用计数的 比如bstr_t, CComPtr, 还有赋值转移拥有权的CAutoPtr。 这些情况比较复杂, 所以容器还是用裸指针简单也安全。

由于时间紧、任务重,就没有空细究了,下面摘一些个人认为讲的不错的片段如下。

按照C++标准,可以存储在任何容器中的对象都必须有CopyConstructible。
CopyConstructible的一个要求就是,假设t是存储在容器中的一个类型为T的对象,那么&t就返回T*,也就是t的地址。而ATL的智能包裹类,如CComBSTr、CCOMPtr等,却都重载了操作符&,CComBSTR类的&操作符返回CComBSTR::m_str,这是一个BSTR类型。这样就破坏了CopyConstructible的要求。
原文中举了一个栗子: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 *’
但VS2015下实测并没有报错。

CComBSTR

BSTR* operator&() throw()
    {
#ifndef ATL_NO_CCOMBSTR_ADDRESS_OF_ASSERT
        ATLASSERT(!*this);
#endif
        return &m_str;
    }

CComPtr

 T** operator&() throw()
    {
        ATLASSERT(p==NULL);
        return &p;
    }

说到这里,我们就不难理解为什么“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模板类。这个模板重载了&操作符,不再让它返回对象的地址。

template <class T>
class CAdapt
{
public:
    CAdapt()
    {
    }
    operator T&()
    {
        return m_T;
    }
    operator const T&() const
    {
        return m_T;
    }
    ……
   T m_T;
};

实际上使用下面的代码似乎也没有出现问题,比如内存泄漏等: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。”

主要参考链接: https://www.cnblogs.com/Clingingboy/archive/2011/06/13/2080077.html
1:https://blog.csdn.net/Virtual_Func/article/details/49724135
2:https://bbs.csdn.net/topics/330192997/
3:https://blog.csdn.net/bearcoding/article/details/45893813

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值