总结MFC窗口销毁过程(转)

 

考虑单窗口情况:

假设自己通过 new 创建了一个窗口对象 pWnd ,然后 pWnd->Create 。则销毁窗口的调用次序:

 

1.       手工调用 pWnd->DestroyWindow() ;

2.       DestroyWindow 会发送 WM_DESTROY ;

3.       WM_DESTROY 对应的消息处理函数是 OnDestroy() ;

4.       DestroyWindow 会发送 WM_NCDESTROY ;

5.       WM_NCDESTROY 对应的消息处理函数是 OnNcDestroy ;

6.       OnNcDestroy 最后会调用 PostNcDestroy ;

7.       PostNcDestroy 经常被用户重载以提供释放内存操作。例如可以使用 delete this ;

通过这种方式,窗口对象对应的窗口和窗口对象本身都被释放了。

如果含有子窗口:

      如果含有子窗口,则调用父窗口的 DestroyWindow 时,它会向子窗口发送 WM_DESTROY 和 WM_NCDESTROY 消息。

      具体调用顺序参考下文的例子。

 

DestroyWindowdelete 的影响:

      应该说前者对后者并没有什么影响。但经常在 DestroyWindow 间接导致执行的 PostNcDestroy 中 delete 窗口对象指针,即 delete this 。

      CView::PostNcDestroy 中唯一的操作就是 delete this ; CframeWnd::PostNcDestory 也是如此。而默认的 CWnd::PostNcDestroy 是空操作, CDialog 中也没有对其进行重载,即也是空。

 

deleteDestroy 的影响:

      delete 会导致析构函数。 CWnd 的析构函数中有对 DestroyWindow 的调用,但必须保证:

m_hWnd != NULL &&

 

           this != (CWnd*) & wndTop && this != (CWnd*)&wndBottom &&

 

           this != (CWnd*)&wndTopMost && this != (CWnd*)&wndNoTopMost

 

      Cdialog 的析构函数中也有对 DestroyWindow 的调用,但条件比较松,只需要 m_hWnd != NULL 。另外 Cdialog::DoModal 也会调用 DestroyWindow

 

      CFrameWnd OnClose 中会调用 DestroyWindow ,但其析构中不会调用 DestroyWindow

 

      CView 的析构也不会调用 DestroyWindow

 

一个 SDI 程序的销毁过程

      有 CMainFrame 类、 CMyView 类。并且 CMyView 有两个子窗口 CMyDlg 和 CmyWnd 的实例。

      点击退出按钮, CMainFrame 会收到 WM_CLOSE 消息。 CframeWnd ( CMainFrame 的父类)间接会调用 CWnd::DestroyWindow ;它首先向 CMyView 发送 WM_DESTORY 和 WM_NCDESTROY 消息,并引发相应的处理函数;然后向 CMyDlg 发送 WM_DESTORY 和 WM_NCDESTROY 消息,并引发相应的处理函数;然后向 CMyWnd 发送 WM_DESTORY 和 WM_NCDESTROY 消息,并引发相应的处理函数。

      具体的执行顺序是:

1.       调用 CMainFrame::DestroyWindow

2.       CFrameWnd::OnDestroy

3.       CMyView::OnDestroy

4.       CmyWnd::OnDestroy

5.       CmyDlg::OnDestroy

6.       CmyWnd::PostNcDestroy

7.       CmyWnd 的析构

8.       CmyDlg::OnDestroy

9.       CmyDlg 的析构

10.   CMyView::PostNcDestroy

11.   CmyView 的析构

12.   CMainFrame 的析构

13.   CMainFrame::DestroyWindow 退出

上面情况是假设我们在 CmyWnd 和 CmyDlg 的 PostNcDestroy 中添加了 delete this 。如果没有添加,则 7 , 10 不会执行。

      因为 CView::PostNcDestroy 中调用了 delete this ,所以然后会执行 CMyView 的析构操作。因为 CframeWnd::PostNcDestroy 中调用了 delete this ,所以最后执行 CMainFrame 的析构操作。

      如果自己的 CmyDlg 和 CmyWnd 在 PostNcDestroy 中有 delete this ;则二者会被析构。否则内存泄漏。当然 delete 也可以放在 CMyView 的析构中做,只是不够 OO 。

总结

      可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过 DestroyWindow 。这是比较好的方法,因为最后 MFC 会自动相应 WM_CLOSE 导致 CframWnd::DestroyWindow 被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在 PostNcDestroy 中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以 delete this 可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。

      另一种是 delete 。 Delete 一个窗口对象指针有的窗口类(如 CWnd , Cdialog )会间接调用 DestroyWindow ,有的窗口类(如 CView , CframeWn )不会调用 DestroyWindow 。所以要小心应对。

      二者是相互调用的,很繁琐。

 

一段很好的文章

一个 MFC 窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在 m_hWnd 成员中的 HWND (窗口句柄),二是窗口对象本身是一个 C++ 对象。要删除一个 MFC 窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。

 

删除窗口最直接方法是调用 CWnd::DestroyWindow 或 ::DestroyWindow ,前者封装了后者的功能。前者不仅会调用后者,而且会使成员 m_hWnd 保存的 HWND 无效 (NULL) 。如果 DestroyWindow 删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用 DestroyWindow 来删除窗口,因为 MFC 会自动调用 DestroyWindow 来删除窗口。例如,当用户退出应用程序时,会产生 WM_CLOSE 消息,该消息会导致 MFC 自动调用 CWnd::DestroyWindow 来删除主框架窗口,当用户在对话框内按了 OK 或 Cancel 按钮时, MFC 会自动调用 CWnd::DestroyWindow 来删除对话框及其控件。

 

窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在 MFC 编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用 new 操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。

 

对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习 C++ 编程时,对 new 操作符的使用往往不太踏实,因为用 new 在堆中创建对象,就不能忘记用 delete 删除对象。读者在学习 MFC 的例程时,可能会产生这样的疑问,为什么有些程序用 new 创建了一个窗口对象,却未显式的用 delete 来删除它呢?问题的答案就是有些 MFC 窗口对象具有自动清除的功能。

 

如前面讲述非模态对话框时所提到的,当调用 CWnd::DestroyWindow 或 ::DestroyWindow 删除一个窗口时,被删除窗口的 PostNcDestroy 成员函数会被调用。缺省的 PostNcDestroy 什么也不干,但有些 MFC 窗口类会覆盖该函数并在新版本的 PostNcDestroy 中调用 delete this 来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用 new 操作符创建在堆中的,但程序员不必操心用 delete 操作符去删除它们,因为一旦调用 DestroyWindow 删除窗口,对应的窗口对象也会紧接着被删除。

 

不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。

 

所有标准的 Windows 控件类。

1.       从 CWnd 类直接派生出来的子窗口对象(如用户定制的控件)。

2.       切分窗口类 CSplitterWnd 。

3.       缺省的控制条类(包括工具条、状态条和对话条)。

4.       模态对话框类。

 

 

具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。

 

1.       主框架窗口类(直接或间接从 CFrameWnd 类派生)。

2.       视图类(直接或间接从 CView 类派生)。

 

 

读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。

 

综上所述,对于 MFC 窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用 DestroyWindow 来删除窗口对象封装的窗口,也不必显式地用 delete 操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的, MFC 的运行机制就可以保证窗口对象的彻底删除。

 

如果需要手工删除窗口对象,则应该先调用相应的函数(如 CWnd::DestroyWindow )删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用 delete 来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用 CWnd::DestroyWindow 即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用 delete 操作符来删除窗口对象.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值