没有足够的时间让析构函数完成?

Posted

技术标签:

【中文标题】没有足够的时间让析构函数完成?【英文标题】:Not enough time for destructor to finish? 【发布时间】:2017-04-13 20:31:08 【问题描述】:

如果我说析构函数突然存在而没有机会完成,因为当所有这些都发生在同一个(主)线程中时 UI 窗口已被取消,这是否有意义?

当我在对话框中按“取消”时,我想释放与列表时间相关的内存:

CMyListCtrl::~CMyListCtrl()

    ItemData* pItemData;
    int nItems = GetItemCount(); <- errors out here and it is called
    for(int i=0; i<nItems; i++)
    
        pItemData = (ItemData*)GetItemData(i);
        if(pItemData != NULL)
            delete pItemData;
    

我的 OnCancel() 处理程序跟随在哪里

LONG CSetupDlg::OnCancel(UINT wParam, LONG lParam)

    ((CSetupDoc *)GetActiveDocument())->Exit();
    return 0;


void CSetupDoc::Exit()

    GetDocTemplate()->CloseAllDocuments(TRUE);

析构函数被调用主要是因为CloseAllDocuments() 调用,但是当它试图清理时它在中间被炸毁,我什至无法通过它。如果我注释 CloseAllDocuments() 调用,那么窗口永远不会被破坏并且析构函数永远不会被调用,所以析构函数肯定与这个函数相关联,它们显然在同一个线程中。

为什么它断言好像底层窗口已经神奇地消失了?

我收到的错误消息是MyApp has triggered a breakpoint,我无法再继续下去了。

【问题讨论】:

请提供更多代码。没有看到执行的每一行,很难调试。 请说明(至少在标签中)您正在做什么。几乎可以肯定是Windows,但那又是什么环境呢? MFC? ATL? 我认为在窗口被销毁之前不会调用析构函数,因此访问控件为时已晚。我不知道为什么这会与CloseAllDocuments 联系在一起。 if(pItemData != NULL) - 没必要。 delete 被定义为对空指针的无操作,所有实现都接受它。 您的问题标题“”建议您认为存在竞争条件。没有。只需调用析构函数,对话框(及其所有子窗口)就不再存在,正如答案已经指出的那样。 【参考方案1】:

您似乎无法从析构函数中访问列表控件的功能。我不记得这是否是规则,或者是否由于您的代码中的其他一些奇怪情况而发生。通常,您可能不能这样做。

无论如何,您可能想尝试在列表控件的WM_DESTROY 消息的处理程序中进行清理,而不是列表控件的析构函数。

【讨论】:

好像是这样,我几乎想说我在很久以前也看到过这种行为。但是从技术上讲,当控件是列表控件的析构函数时,它仍然不应该被销毁。 zar 是对的——你的类中的清理覆盖CMyListCtrl::DestroyWindow,然后在清理完成后调用CListCtrl::DestroyWindow。您通常不会在 MFC 析构函数中执行任何需要有效窗口的操作,因为 CWnd 对象封装了数据,但并不等同于实际窗口。 我建议你在你要添加的WM_DESTROY处理函数上放一个断点;然后当它被命中时,创建一个数据断点(在断点窗格上),其条件为listcontrol-&gt;m_hWnd 更改。在某个时刻,变量将变为空,执行将在那里暂停。然后,您可以单独执行每个相关步骤,这样您就可以更好地了解整个过程是如何发生的。顺便说一句,如果您“甚至无法通过它”,可能是您没有加载相应的符号。【参考方案2】:

你等得太久了。在析构函数中为时已晚。此时,窗口句柄 (m_hWnd) 不再有效,Windows 已销毁该窗口。此时列表控件中的项目已全部删除。正如其他人所指出的,将您自己的 CMyListCtrl::OnDestroy() 函数和 ON_WM_DESTROY() 宏添加到您的 CMyListCtrl 消息映射中。将清理代码放在 OnDestroy() 函数中。

【讨论】:

试过了,虽然它没有在那里断言,但 GetItemCount() 返回零并且对话框已经消失了,所以看起来列表已经被破坏了。 哦我想起来了,它必须在 WM_DELETEITEM 上删除! 通常情况下,我不会像你那样做。通常,集合存在于控件的生命周期之外。在这种情况下,当我调用 SetItemDataPtr() 时,我正在设置一个索引、指针、迭代器或对集合中不需要由控件清理的项的其他引用。【参考方案3】:

我发布了解决问题的正确答案。我们需要在LVN_DELETEITEM 消息处理程序中释放列表内存(即使OnDestroy() 不起作用,那时列表已经消失了)。

void CMyListCtrl::OnLvnDeleteitem(NMHDR *pNMHDR, LRESULT *pResult)

    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    // TODO: Add your control notification handler code here

    ItemData* pItemData = (ItemData* )pNMLV->lParam;
    delete pItemData;

    *pResult = 0;

就析构函数断言/不进一步执行而言,是因为GetItemCount() 在列表window 上运行,但由于它已经被销毁,因此行为未知。

【讨论】:

以上是关于没有足够的时间让析构函数完成?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 类设计总结回顾------析构函数

C++学习24 虚析构函数

C++中的纯虚析构函数

构造函数与析构函数

使用析构函数完成任务

Python析构函数