TN017:销毁窗口对象

此说明介绍了该方法的使用 CWnd::PostNcDestroy 。 如果要对派生对象进行自定义分配 CWnd,请使用此方法。 此说明还说明了为何应使用 CWnd::DestroyWindow 它来销毁C++ Windows 对象而不是 delete 运算符。

如果遵循本文中的准则,将很少出现清理问题。 这些问题可能会导致诸如忘记删除/释放C++内存、忘记释放系统资源(例如 HWNDs)或释放对象过多等问题。

问题

每个窗口对象(派生自 CWnd的类的对象)都表示C++对象和一个 HWND。 C++对象在应用程序的堆中分配,并且 HWND由窗口管理器在系统资源中分配。 由于有多种方法来销毁窗口对象,因此我们必须提供一组规则来防止系统资源或内存泄漏。 这些规则还必须防止对象和 Windows 句柄被多次销毁。

正在销毁窗口

以下是销毁 Windows 对象的两种允许方法:

  • 调用 CWnd::DestroyWindow 或 Windows API DestroyWindow

  • 使用 delete 运算符显式删除。

第一种情况是迄今为止最常见的情况。 即使代码未直接调用 DestroyWindow ,也适用这种情况。 当用户直接关闭框架窗口时,此作将生成WM_CLOSE消息,对此消息的默认响应是调用 DestroyWindow。 当父窗口被销毁时,Windows 将调用 DestroyWindow 其所有子窗口。

第二种情况( delete 在 Windows 对象上使用运算符)应很少见。 下面是使用 delete 正确选择的一些情况。

使用 CWnd::PostNcDestroy

当系统销毁 Windows 窗口时,发送到窗口的最后一条 Windows 消息为 WM_NCDESTROY。 该消息的默认 CWnd 处理程序为 CWnd::OnNcDestroyOnNcDestroy 将分离 HWND C++对象并调用虚拟函数 PostNcDestroy。 某些类重写此函数以删除C++对象。

默认实现 CWnd::PostNcDestroy 不执行任何作,这适用于在堆栈帧上分配的窗口对象或嵌入到其他对象中。 此行为不适用于设计用于在堆上分配的窗口对象,而无需任何其他对象。 换句话说,它不适用于未嵌入在其他C++对象中的窗口对象。

专为在堆上单独分配而设计的类会重写 PostNcDestroy 要执行的方法 delete this;。 此语句将释放与C++对象关联的任何内存。 即使默认 CWnd 析构函数调用 DestroyWindow (如果 m_hWnd 不是) NULL也是如此,此调用不会导致无限递归,因为句柄将分离, NULL 并在清理阶段。

注释

系统通常在处理 Windows WM_NCDESTROY 消息和HWNDC++窗口对象后调用CWnd::PostNcDestroy。 如果发生故障,系统还会在大多数CWnd::Create调用的实现中调用CWnd::PostNcDestroy。 本文稍后将介绍自动清理规则。

自动清理类

以下类不用于自动清理。 它们通常嵌入到其他C++对象或堆栈中:

  • 所有标准 Windows 控件(CStaticCEditCListBox等等)。

  • 直接派生自 CWnd 的任何子窗口(例如自定义控件)。

  • 拆分窗口 (CSplitterWnd)。

  • 默认控制栏(派生自 CControlBar的类,请参阅 技术说明 31 ,以启用控件栏对象的自动删除)。

  • 专为堆栈帧上的模式对话设计的对话框 (CDialog)。

  • 除 .. 之外 CFindReplaceDialog的所有标准对话。

  • ClassWizard 创建的默认对话。

以下类专为自动清理而设计。 它们通常由自己在堆上分配:

  • 主框架窗口(直接或间接派生自 CFrameWnd)。

  • 查看窗口(直接或间接派生自 CView)。

如果要中断这些规则,则必须重写 PostNcDestroy 派生类中的方法。 若要向类添加自动清理,请调用基类,然后执行作 delete this;。 若要从类中删除自动清理,请直接调用 CWnd::PostNcDestroy 而不是 PostNcDestroy 直接基类的方法。

更改自动清理行为的最常见用途是创建可在堆上分配的无模式对话。

何时调用 delete

建议调用 DestroyWindow 以销毁 Windows 对象(C++ 方法或全局 DestroyWindow API)。

不要调用全局 DestroyWindow API 来销毁 MDI 子窗口。 应改用虚拟方法 CWnd::DestroyWindow

对于不执行自动清理的C++ Window 对象,如果在未指向正确派生类的情况下尝试在析构函数VTBLCWnd::~CWnd调用DestroyWindow,则使用delete运算符可能会导致内存泄漏。 发生泄漏的原因是系统找不到要调用的适当销毁方法。 使用 DestroyWindow 而不是 delete 避免这些问题。 由于此错误可能很微妙,因此如果面临风险,在调试模式下编译将生成以下警告。

Warning: calling DestroyWindow in CWnd::~CWnd
    OnDestroy or PostNcDestroy in derived class will not be called

对于执行自动清理的C++ Windows 对象,必须调用 DestroyWindow。 如果直接使用 delete 运算符,MFC 诊断内存分配器将通知你释放内存两次。 这两个匹配项是在自动清理实现PostNcDestroy中首次显式调用和间接调用delete this;

调用 DestroyWindow 非自动清理对象后,C++对象仍将在周围,但 m_hWnd 将是 NULL。 调用 DestroyWindow 自动清理对象后,C++对象将消失,由自动清理实现 PostNcDestroy中的 C++ delete 运算符释放。

另请参阅

按编号列出的技术说明
按类别列出的技术说明