【问题标题】:What is the proper lifetime management for a CCmdTarget used as an event sink?用作事件接收器的 CCmdTarget 的正确生命周期管理是什么?
【发布时间】:2011-10-13 18:39:15
【问题描述】:

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

让我很满意的是样本的最后一个音符:

由于 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(不是循环的先前迭代使用的某个先前实例)。效果是当前sink在当前循环执行下被删除了,导致一堆空指针错误。

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

我想我可能会为循环的每次迭代重复使用相同的接收器实例,但我很好奇正确的生命周期管理是什么。我是否需要在自己删除实例和覆盖 OnFinalRelease 以不执行任何操作与从不删除自己并始终期望 OnFinalRelease 执行删除之间做出明确的选择?一个比另一个更喜欢吗?

【问题讨论】:

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


    【解决方案1】:

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

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

    更新:

    如果默认实现是不可取的,就像 MFC 的情况一样,那么最好的选择是覆盖 virtual OnFinalRelease 并让它什么都不做,而不是像在 CCmdTarget 中那样调用 delete this;。然后完全由您自己删除对象并进行清理。如果你有 C++0x 支持,我会让你的 sink 类引用计数对象,或者把它放在像std::unique_ptr 这样的智能指针中。

    【讨论】:

    • 我支持的代码有子类 CCmdTarget(类 CMyEventHandler : public CCmdTarget)。令人不安的是对 OnFinalRelease 的调用是不可靠的。如果我在调用 AfxConnectionUnadvise 后显式删除我的 CMyEventHandler 实例,我会看到看似随机调用的 OnFinalRelease 函数。如果我没有明确删除我的 CMyEventHandler 实例,则永远不会调用 OnFinalRelease 方法。这会导致内存泄漏。
    • 当您可以编辑现有问题时,尽量不要添加新问题:stackoverflow.com/questions/6845691/…
    • 同意,这两个问题是相关的,但他们提出的问题略有不同。在这里,我想知道对象的适当生命周期管理是什么(具体来说,我自己删除对象是否正确,还是应该允许 OnFinalRelease 处理它)。在那里,我想知道是什么导致 OnFinalRelease 被调用,因为它的行为似乎不可靠。感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 2018-07-10
    • 2012-12-28
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多