此说明介绍了该方法的使用 CWnd::PostNcDestroy
。 如果要对派生对象进行自定义分配 CWnd
,请使用此方法。 此说明还说明了为何应使用 CWnd::DestroyWindow
它来销毁C++ Windows 对象而不是 delete
运算符。
如果遵循本文中的准则,将很少出现清理问题。 这些问题可能会导致诸如忘记删除/释放C++内存、忘记释放系统资源(例如 HWND
s)或释放对象过多等问题。
问题
每个窗口对象(派生自 CWnd
的类的对象)都表示C++对象和一个 HWND
。 C++对象在应用程序的堆中分配,并且 HWND
由窗口管理器在系统资源中分配。 由于有多种方法来销毁窗口对象,因此我们必须提供一组规则来防止系统资源或内存泄漏。 这些规则还必须防止对象和 Windows 句柄被多次销毁。
正在销毁窗口
以下是销毁 Windows 对象的两种允许方法:
调用
CWnd::DestroyWindow
或 Windows APIDestroyWindow
。使用
delete
运算符显式删除。
第一种情况是迄今为止最常见的情况。 即使代码未直接调用 DestroyWindow
,也适用这种情况。 当用户直接关闭框架窗口时,此作将生成WM_CLOSE消息,对此消息的默认响应是调用 DestroyWindow
。 当父窗口被销毁时,Windows 将调用 DestroyWindow
其所有子窗口。
第二种情况( delete
在 Windows 对象上使用运算符)应很少见。 下面是使用 delete
正确选择的一些情况。
使用 CWnd::PostNcDestroy
当系统销毁 Windows 窗口时,发送到窗口的最后一条 Windows 消息为 WM_NCDESTROY
。 该消息的默认 CWnd
处理程序为 CWnd::OnNcDestroy
。
OnNcDestroy
将分离 HWND
C++对象并调用虚拟函数 PostNcDestroy
。 某些类重写此函数以删除C++对象。
默认实现 CWnd::PostNcDestroy
不执行任何作,这适用于在堆栈帧上分配的窗口对象或嵌入到其他对象中。 此行为不适用于设计用于在堆上分配的窗口对象,而无需任何其他对象。 换句话说,它不适用于未嵌入在其他C++对象中的窗口对象。
专为在堆上单独分配而设计的类会重写 PostNcDestroy
要执行的方法 delete this;
。 此语句将释放与C++对象关联的任何内存。 即使默认 CWnd
析构函数调用 DestroyWindow
(如果 m_hWnd
不是) NULL
也是如此,此调用不会导致无限递归,因为句柄将分离, NULL
并在清理阶段。
注释
系统通常在处理 Windows WM_NCDESTROY
消息和HWND
C++窗口对象后调用CWnd::PostNcDestroy
。 如果发生故障,系统还会在大多数CWnd::Create
调用的实现中调用CWnd::PostNcDestroy
。 本文稍后将介绍自动清理规则。
自动清理类
以下类不用于自动清理。 它们通常嵌入到其他C++对象或堆栈中:
所有标准 Windows 控件(
CStatic
、CEdit
、CListBox
等等)。直接派生自
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 对象,如果在未指向正确派生类的情况下尝试在析构函数VTBL
中CWnd::~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 运算符释放。