考虑单窗口情况:

  假设自己通过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消息。

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

  DestroyWindow对delete的影响:

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

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

  delete对Destroy的影响:

  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操作符来删除窗口对象.

MFC DestroyWindow窗口对象和窗口句柄的销毁的更多相关文章

  1. MFC永久窗口对象与临时窗口对象

    这篇讲得很清楚,就转过来了,原文如下: 因项目需要,最近在学习MFC,下午在一篇教程中提到了临时窗口.永久窗口,作者让读者自行查阅MSDN,了解临时窗口与永久窗口的概念,出于好奇,出于方便,直接百度一 ...

  2. MFC中应用对象的成员:窗口指针m_pMainWnd说明

    CVC_MFC_firstDlg dlg; //定义对话框对象m_pMainWnd = &dlg;  //这个定义的对话框 dlg 成为主窗口 应用程序对象成员变量m_pMainWnd是一个窗 ...

  3. MFC DestroyWindow[转]

    考虑单窗口情况: 假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create.则销毁窗口的调用次序: 1. 手工调用pWnd->DestroyWindow(): 2. De ...

  4. MFC子窗口和父窗口

    转载声明: 本文转载自:http://www.cnblogs.com/BeyondTechnology/archive/2011/03/25/1995934.html 感谢BeyondTechnolo ...

  5. MFC子窗口和父窗口(SetParent,SetOwner)

    一.概念和区别 在windows系统中,每个窗口对象都对应有一个数据结构,形成一个list链表.系统的窗口管理器通过这个list来获取窗口信息和管理每个窗口.这个数据结构中有四个数据用来构建list, ...

  6. duilib底层机制剖析:窗口类与窗口句柄的关联

    转载请说明原出处.谢谢~~ 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术. 这些技术都是为了解决c++封装的窗口类与窗口句柄的关联问题. 这里是三篇关于thunk技 ...

  7. VS2010 MFC中 窗口分割的实现

    分割窗口概述 分割窗口,顾名思义,就是将一个窗口分割成多个窗格,在每个窗格中都包含有视图,或者是同一类型的视图,或者是不同类型的视图. MFC分割窗口的方式有两种,动态分割和静态分割. 动态分割窗口通 ...

  8. MFC基础窗口创建,CWinApp、CFrameWnd

    1.CWinApp(包括了这个类的导出类):代表了我们的程序.封装了消息循环等. 2.CFrameWnd:代表了程序的框架窗口.封装了窗口的注册.创建.显示.刷新.等等窗口操作. 3.Win32中.一 ...

  9. C#中关闭子窗口而不释放子窗口对象的方法

    1 在主窗口中实例化子窗口 在主窗口中实例化子窗口,而不是在按钮中实例化子窗口对象. Form2 f2 = new Form2(); 2 通过按钮来显示主窗口 在按钮中需要实现的是窗口的显示 priv ...

随机推荐

  1. Raspberrypi安装使用开发简要说明

    Raspberrypi安装使用开发简要说明 (更新于2013年8月25日 newsuppy) 一,安装 使用win32diskimager将操作系统的image刷在SD卡上,image文件可以在htt ...

  2. Compound class names are not supported. Consider searching for one class name and filtering the results

    原文地址:http://stackoverflow.com/questions/20361643/compound-class-names-are-not-supported-consider-sea ...

  3. 通过实现Comparable接口结合TreeSet来对对象自动排序

    经过会遇到这样的情况,对于某个对象数组或者链表要按照一定的规则进行排序,那么我们该怎么做呢? 如遇到这样的需求: 1.需求1 对于学生对象按照年龄进行排序,年龄小的排在前面. 单单看到这样的需求,实现 ...

  4. poj2739

                                                                                           Sum of Consec ...

  5. handsontable插件事件

    Hook插件 afterChange (changes: Array, source: String):1个或多个单元格的值被改变后调用     changes:是一个2维数组包含row,prop,o ...

  6. Spark函数详解系列之RDD基本转换

    摘要:   RDD:弹性分布式数据集,是一种特殊集合 ‚ 支持多种来源 ‚ 有容错机制 ‚ 可以被缓存 ‚ 支持并行操作,一个RDD代表一个分区里的数据集   RDD有两种操作算子:         ...

  7. Python网络爬虫(6)--爬取淘宝模特图片

    经过前面的一些基础学习,我们大致知道了如何爬取并解析一个网页中的信息,这里我们来做一个更有意思的事情,爬取MM图片并保存.网址为https://mm.taobao.com/json/request_t ...

  8. 【SQL学习笔记】排名开窗函数,聚合开窗函数(Over by)

    处理一些分组后,该组按照某列排序后 ,取其中某条完整数据的问题. 或 按照其中不同列分组后的聚合 比如 sum,avg之类. MSDN上语法: Ranking Window Functions < ...

  9. 一些基础的.net用法

    一.using 用法 using 别名设置 using 别名 = System.web 当两个不同的namespace里有同名的class时.可以用 using aclass = namespace1 ...

  10. CSS基础笔记

    之前没有开通好博客,笔记都记录在有道云,今天全部转过来!!! 1.当同一个html元素不止一个样式定义时,内联样式(在html元素内部)拥有最高的优先权:其他如内部样式表(位于<head> ...