此章节讲述了CWnd::PostNcDestroy成员函数的使用。如果你想对派生自CWnd的对象做自定义的处理,你可以使用这个函数。
此章节也阐述了“要销毁一个C++的Windows对象,使用DestroyWindow而不是delete”这个最重要规则的原因。
这是非常重要的。如果你遵照以下的指导方法,你就会很少碰到清除方面的问题(例如忘记删除/释放C++内存,忘记释放系统资源比如说HWNDs,或者释放对象太多次)。
问题
Windows对象(CWnd派生类的对象)既代表一个C++对象(在应用程序的堆中分配)也代表了一个HWND(由窗口管理器在系统资源里分配)。由于存在多种途径来销毁一个窗口对象,我们必须提供一组规则以防止系统资源或者应用程序的内存泄露问题,同时也防止对象和Windows句柄被多次销毁。
这不仅仅是一个内存管理的问题。一个Windows窗口的存在会对用户界面产生影响:窗口绘制在屏幕上;一旦它被销毁,同样对系统资源也会产生影响。在你的应用程序地址空间里泄漏C++内存并不会像泄漏系统资源那样糟糕。
销毁窗口
有两种方法被允许来销毁一个Windows对象:
调用CWnd::DestroyWindow或Windows API ::DestroyWindow.
利用delete操作符来进行明确的删除工作。
第一种方法是迄今为止最常用的。即使DestroyWindow没有在你的代码里被直接调用,此方法也照常适用。这种情况就是,当用户直接关闭一个框架窗口时(缺省的WM_CLOSE行为主是调用DestroyWindow),当一个父窗口(框架窗口)被销毁时,Windows会调用DestroyWindow来销毁它的所有的子窗口。
注意 CWnd::PostNcDestroy一般会在Windows消息WM_NCDESTORY处理后被调用,把它作为窗口销毁的一部分,同时HWND和C++窗口对象不再关联。
CWnd::PostNcDestroy也会在大部分Create调用的执行部分被调用,如果错误发生的话(自动清理规则如下)
自动清理类
以下的这些类不是设计来做自动清理的。他们通常嵌在其他C++对象或者在堆栈上:
l 所有的标准Windows控件(CStatic, CEdit, ClistBox等)
l 所有从CWnd直接派生来的子窗口(比例,自定义控件)
l 拆分窗口(CSplitterWnd)
l 缺省控制条(从CcontrolBar派生的类,查看TN31来了解能够自动删除的控制条对象)
l 对话框(CDialog)设计来在堆栈上创建模态对话框
l 所有的标准对话框,除了CfindReplaceDialog
l 由ClassWizard创建的缺省对话框
如果你想打破任何一条规则,你就必须在你的派生类中重载PostNcDestroy成员函数。为了增加自动清理到你的类,只需要调用你的基类并做delete this操作。为了将自动清理从你的类中移出,直接调用CWnd::PostNcDestroy来代替你基类的成员函数PostNcDestory.
以上内容常用在创建一个能在堆上分配的非模态的对话框。
何时调用delete
销毁一个窗口最好是调用DestoryWindow,不管是C++的成员函数还是全局的::DestoryWindow API.
不要调用全局的API ::DestroyWindow来销毁一个MDI子窗口,使用虚拟成员函数CWnd::DestroyWindow来代替它。
对于那些不执行自动清理的C++窗口对象,使用DestoryWindow来代替delete以避免你必须在CWnd::~CWnd析构函数中调用DestoryWindow的问题,而在此处VTBL并没有指向正确的派生类。这可能会导致许多bugs,所以MFC诊断版本(调试)中会警告你:
“Warning: calling DestroyWindow in CWnd::~CWnd
OnDestroy or PostNcDestroy in derived class will not be called”
对于执行自动清理工作的C++Windows对象,你必须调用DestroyWindow。如果你直接使用操作符delete,MFC的诊断内存分配算符将会警告你:你正在第二次释放内存(第一次调用delete,还有在PostNcDestroy的自动清理执行过程中调用delete this)。
对于一个不执行自动清理的对象,在调用DestroyWindow之后,这个C++对象仍然存在,但是m_hWnd会为空。对一个执行自动清理的对象,在调用DestroyWindow之后,此C++对象就不存在了,它被PostNcDestroy的自动清理执行过程里的delete操作符释放。
以下这些类设计来做自动清理。他们一般单独分配在堆上:
主框架窗口(直接或间接派生于CFrameWnd)
视图窗口(直接或间接派生于CView)
利用CWnd::PostNcDestroy进行自动清除
当销毁一个Windows窗口时,最后发送给此窗口的Windows消息是WM_NCDESTROY。CWnd对此消息的缺省处理(CWnd::OnNcDestroy)会将C++对象与HWND分离,并调用虚函数PostNcDestroy。一些类重载这个函数来删除C++对象。
CWnd::PostNcDestroy的缺省操作是什么也不做,这适合于那些分配在堆栈或者嵌在其他对象里面的窗口对象。这不适合于那些设计来分配在堆上的窗口对象(不嵌在其他C++对象中)。
那些设计来分配在堆上的类可以重载成员函数PostNcDestroy以执行“delete this”操作。它将会释放任何与此C++对象相关的C++内存。尽管缺省的CWnd析构函数会在m_hWnd不为空的情况下调用DestoryWindow,但这不会导致无穷递归,因为此句柄在清除阶段将会处于分离状态并为空。