注意COM对象的析构函数

如果你在析构函数中做了很多的事,你将会有一些麻烦。

举个例子,假如你在析构函数中调用了一些其它函数,而这些函数内部操作中可能又会调用到IUnknown::AddRef IUnknown::Release方法。考虑下面这个代码:

ULONG MyObject::Release()

{

 LONG cRef = InterlockedDecrement(&m_cRef);

 if (cRef == 0) {

  delete this;

 }

 return cRef;

}

 

MyObject::~MyObject()

{

 if (m_fNeedSave) Save();

}

看起来这个代码中没有问题。我们在释放函数中执行保存对象功能。

但是,这个保存函数可能如下:

HRESULT MyObject::Save()

{

 CComPtr<IStream> spstm;

 HRESULT hr = GetSaveStream(&spstm);

 if (SUCCEEDED(hr)) {

  CComQIPtr<IObjectWithSite, &IID_IObjectWithSite> spows(spstm);

  if (spows) spows->SetSite(this);

  hr = SaveToStream(spstm);

  if (spows) spows->SetSite(NULL);

 }

 return hr;

}

这看起来也没有什么问题。取得一个IStream并且执行保存,其中把this作为SetSite的参数。

事实上这会带来一些问题,请看下面的分析:

  • Release() 函数把引用计数减到0,并执行delete this操作。
  • Save() 函数取得IStream并且把this指针作为参数传给SetSite。引用计数从0变成了1
  • SaveToStream() 函数保存对象。
  • Save() 函数返回执行清除,引用计数从1又回到了0
  • Release() 函数又进入到析构函数。

因此,至少,你应该在你的AddRef()函数中增加一个assert,以保证你的引用计数不能从0上增加。

ULONG MyObject::AddRef()

{

 assert(m_cRef != 0);

 return InterlockedIncrement(&m_cRef);

}

你可能感兴趣的:(注意COM对象的析构函数)