用作事件接收器的CCmdTarget的正确生命周期管理是什么?

时间:2011-07-26 21:05:20

标签: .net c++ visual-c++ com mfc

我正在关注为"How to create a sink interface in a MFC-based COM client"提供的Microsoft示例代码,以便在C ++(VC6)中创建事件接收器。事件源是一个.NET程序集,通过COM interop公开它的功能。

给我适合的东西是样本的最后一个注释:

  

因为CMySink是在堆上创建的,所以请确保将其删除   避免内存泄漏。

我注意到的一些事情:

  • GetIDispatch和AfxConnectAdvise / AfxConnectUnadvise的自动递增/递减参数设置为FALSE,因此我假设在整个练习过程中接收器的内部引用计数保持不变。
  • 练习中未显示OnFinalRelease方法,因此我假设它是删除接收器实例的默认行为。

请记住示例文本中的最后一个注释,我的清理代码如下所示:

//Get a pointer to sinks IUnknown, no AddRef.
LPUNKNOWN pUnkSink = m_pSink->GetIDispatch(FALSE);

//Terminate a connection between source and sink.
//m_pUnkSrc is IUnknown of server obtained by CoCreateInstance().
//m_dwCookie is a value obtained through AfxConnectionAdvise().
AfxConnectionUnadvise(m_pUnkSrc, IID_MYEVENT, pUnkSink, FALSE,
   m_dwCookie);
delete m_pUnkSink;

此示例所属的代码在循环中运行,该循环涉及创建接收器,连接它,等待几个事件,然后将其拆除并删除它。我所看到的是,经过几轮循环后,OnFinalRelease被突然调用。不仅如此,OnFinalRelease被调用于接收器的实例,用于循环的当前迭代(不是先前迭代循环使用的先前实例)。结果是从当前循环执行中删除了当前接收器,并导致一堆空指针错误。

我尝试删除删除m_pUnkSink的调用。结果是永远不会调用OnFinalRelease。这给我留下了内存泄漏,因为所有这些接收器实例都在堆中累积。

我想我可能会在循环的每次迭代中重复使用接收器的相同实例,但我很好奇正确的生命周期管理是什么。我是否需要在自己删除实例和重写OnFinalRelease之间做出明确的选择而不做任何事情而不是永远删除自己并且总是期望OnFinalRelease执行删除?一个人更喜欢另一个吗?

1 个答案:

答案 0 :(得分:1)

OnFinalRelease的默认实现是删除对象。最好将CCmdTarget子类化为获取消息映射和事件挂钩,并创建自己的类,它可以保留所需的所有处理范围,然后只删除一次。您可以添加一个回调或通知消息,指示正在完成所有操作以清理对象。

出于性能原因,您不希望在循环中从堆中分配和删除,如果没有别的话。

<强>更新

如果默认实现是不合需要的,就像通常使用MFC一样,那么最好的选择是覆盖virtual OnFinalRelease并让它不做任何事情,而不是像delete this;那样调用CCmdTarget。 1}}。然后完全取决于您删除您的对象并自行清理。如果您有C ++ 0x支持,我会将您的接收器类引用计数对象或将其放入某种类型的智能指针,如std::unique_ptr