MFC DestroyWindow[转]

考虑单窗口状况:

  假设本身经过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操做符来删除窗口对象.程序员

相关文章
相关标签/搜索