Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363506
  • 博文数量: 56
  • 博客积分: 2721
  • 博客等级: 中校
  • 技术积分: 460
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-29 16:18
文章分类

全部博文(56)

文章存档

2014年(1)

2011年(15)

2010年(5)

2008年(35)

分类: C/C++

2011-01-08 10:49:50

TN017: 销毁窗口对象

This note describes the use of the CWnd::PostNcDestroy member function. Use this function if you wish to do customized allocation of CWnd-derived objects.

This note also explains some of the reasons for the cardinal rule:

To destroy a C++ Windows object, use DestroyWindow, not "delete".

本文描述了在定制CWnd对象的分配行为时,成员函数CWnd::PostNcDestroy的使用方法。同时,说明了销毁C++窗口,使用DestoryWindow,而非”delete”的原因。

This is important. If you follow the guidelines below, you will have few cleanup problems (such as forgetting to delete/free C++ memory, forgetting to free system resources like HWNDs, or freeing objects too many times).

这一点很重要。跟随以下的指引,清除工作将不会有什么问题(诸如忘记删除或释放C++内存,忘记释放系统资源,例如HWNDs,或者多次释放同一个对象)。

The Problem

问题的提出

Windows objects (objects of classes derived from CWnd) represent both a C++ object (allocated in the application's heap) and an HWND (allocated in system resources by the window manager). Since there are several ways to destroy a window object, we must provide a set of rules that prevent system resource or application memory leaks and that prevent objects and Windows handles from being destroyed more than once.

Windows对象(CWnd派生类的对象)即可以看作是一个C++对象(分配在应用程序堆里),又可以看作是HWND(由窗口管理器在系统资源里分配)。由于有多种方法来销毁一个窗口对象,我们必须提供一组规则来防止系统资源或者应用程序内存泄露,保证对象和Windows句柄不被多次释放。

This is more than a memory management problem. The presence of a Windows window has user-interface impact: a window drawn on the screen; once it is destroyed, there is also an impact on system resources. Leaking C++ memory in your application address space is not as bad as leaking system resources.

这不仅仅是内存管理的问题。首先,Windows窗口的出现会对用户看到的界面产生影响:窗口是被绘制到屏幕上的;其次,一旦窗口被销毁,这将对系统资源产生影响。系统资源泄露的危害要比应用程序地址空间发生内存泄露的危害大得多。

Destroying Windows

销毁窗口

The two permitted ways to destroy a Windows object are:

l Calling CWnd::DestroyWindow or the Windows API ::DestroyWindow.

l Explicitly deleting with the delete operator.

销毁一个窗口对象有两种方法:

l 调用CWnd::DestroyWindow或者Windows API ::DestroyWindow

l 使用delete操作符显式删除。

The first case is by far the most common. This case applies even if is not called directly by your code. This is the case when the user directly closes a frame window (the default WM_CLOSE behavior is to call DestroyWindow), and when a parent window is destroyed, Windows calls DestroyWindow for all the children.

第一种情况比较常见,即使代码里没有直接调用DestroyWindow函数DestroyWindow也会被间接调用。例如,当用户直接关闭框架窗口(WM_CLOSE的默认实现是调用DestroyWindow)或者当一个父窗口被销毁时,Windows调用DestroyWindow函数来销毁它所有的子窗口。

The second case, the use of the delete operator on Windows objects, should be very rare and only in the cases outlined below.

第二种情况,即使用delete操作符销毁窗口对象,应尽量少用,并且仅在以下情况下使用。

Auto Cleanup with

使用CWnd::PostNcDestroy自动销毁

When destroying a Windows window, the last Windows message sent to the window is WM_NCDESTROY. The default CWnd handler for that message (CWnd::OnNcDestroy) will detach the HWND from the C++ object and call the virtual function PostNcDestroy. Some classes override this function to delete the C++ object.

当销毁一个Windows窗口时,最后发送给此窗口的Windows消息是WM_NCDESTROYCWnd类处理该消息的函数(CWnd::OnNcDestroy)默认地会将HWNDC++对象解绑,然后调用虚函数PostNcDestroy,有些类会重载该函数来释放(delete)此C++对象。

The default implementation of CWnd::PostNcDestroy does nothing which is appropriate for window objects allocated on the stack frame or embedded in other objects. This is not appropriate for window objects that are designed to be allocated by themselves on the heap (not embedded in other C++ object).

默认的CWnd::PostNcDestroy实现并不做任何处理,这适用于在栈上创建的或者嵌入其它对象中的窗口对象。但对于在堆上创建(没有嵌入其它C++对象中)的窗口对象不适用。

Those classes that are designed to be allocated by themselves on the heap override the PostNcDestroy member function to perform a . This statement will free any C++ memory associated with the C++ object. Even though the default CWnd destructor calls DestroyWindow if m_hWnd is non-NULL, this does not lead to infinite recursion since the handle will be detached and NULL during the cleanup phase.

如果需要在堆上创建窗口对象,需要在类中重载PostNcDestroy函数,并执行"delete this"操作,这将释放掉该C++对象所占用的C++内存。尽管缺省的CWnd析构函数会在m_hWnd不为空的情况下调用DestoryWindow,但这不会导致无穷递归,因为此句柄在清除阶段将会处于分离状态并置为空。

Note

CWnd::PostNcDestroy is normally called after the Windows WM_NCDESTROY message is processed, as part of window destruction, and the HWND and the C++ window object are no longer attached. CWnd::PostNcDestroy will also be called in the implementation of most Create calls if failure occurs (see below for auto cleanup rules).

注意

CWnd::PostNcDestroy正常情况下会在WM_NCDESTROY消息被处理之后,作为窗口销毁的一部分被调用,此时,HWNDC++窗口对象已不再关联。在大多数的Create实现中,如果发生错误CWnd::PostNcDestroy也会被调用(参见以下自动清理规则)。

Auto Cleanup Classes

自动清理类

The following classes are not designed for auto-cleanup. They are normally embedded in other C++ object or on the stack:

l All standard Windows controls (CStatic, CEdit, CListBox, and so on).

l Any child windows derived directly from CWnd (for example, custom controls).

l Splitter windows (CSplitterWnd).

l Default control bars (classes derived from CControlBar, see Technical Note 31 for enabling auto-delete for control bar objects).

l Dialogs (CDialog) designed for modal dialogs on the stack frame.

l All the standard dialogs except CFindReplaceDialog.

l          The default dialogs created by ClassWizard.

以下的类并不会自动清理,它们通常在栈中创建或者嵌入其它的C++对象中:

l 所有Windows标准控件(CStaticCEditCListBox等)。

l 所有从CWnd直接派生来的子窗口(例如,自定义控件)。

l 拆分窗口(CSplitterWnd)。

l 默认的控件栏(从CControlBar派生的类,请参照Technical Note 32来实现控件栏对象的自动删除)。

l 在栈上创建的模态对话框(CDialog)。

l 所有的标准对话框,除了CFindReplaceDialog

l ClassWizard创建的缺省对话框。

The following classes are designed for auto-cleanup. They are normally allocated by themselves on the heap:

l Main frame windows (derived directly or indirectly from CFrameWnd).

l View windows (derived directly or indirectly from CView).

下面的类可以自动清理,它们一般在堆上创建:

l 主框架窗口(直接或者间接派生至CFrameWnd)。

l 视图窗口(直接或者间接派生至CView)。

If you wish to break any of these rules, you must override the member function in your derived class. To add auto-cleanup to your class, call your base class and then do a . To remove auto-cleanup from your class, call CWnd::PostNcDestroy directly instead of the PostNcDestroy member in your direct base class.

The most common use of the above is to create a modeless dialog that can be allocated on the heap.

如果你想打破以上任何一条规则,你必须在你的派生类中重载PostNcDestroy成员函数。若要为你的类添加自动清理功能,需要调用基类的PostNcDestroy,然后执行delete this操作。若要去除自动清理功能,直接调用CWnd::PostNcDestroy来代替你基类的成员函数PostNcDestory。这些规则,主要用于在堆上创建非模态对话框。

When to Call 'delete'

何时调用'delete'

The recommended way to destroy a Windows object is to call DestroyWindow, either the C++ member function or the global ::DestroyWindow API.

Do not call the global ::DestroyWindow API to destroy an MDI Child window; use the vi CWnd::DestroyWindow instead.

我们推荐使用DestroyWindow函数(C++成员函数或者全局的::DestroyWindow API)来销毁窗口对象。

销毁MDI子窗口,要调用虚拟成员函数CWnd::DestroyWindow,而不能调用全局的::DestroyWindow API

For C++ Window objects that do not perform auto-cleanup, using DestroyWindow instead of delete avoids problems of having to call DestroyWindow in the CWnd::~CWnd destructor where the VTBL is not pointing to the correctly derived class. This can lead to subtle bugs so the diagnostic (debug) version of MFC will warn you with

   OnDestroy or PostNcDestroy in derived class will not be called

对于那些不执行自动清理的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

In the case of C++ Windows objects that do perform auto-cleanup, you must call DestroyWindow. If you use operator delete directly, the MFC diagnostic memory allocator will alert you that you are freeing memory twice (the first call to delete as well as the indirect call to "delete this" in the auto-cleanup implementation of PostNcDestroy).

对于执行自动清理工作的C++ Windows对象,你必须调用DestroyWindow。如果你直接使用操作符deleteMFC的诊断内存分配算符将会警告你:你正在第二次释放内存(第一次调用delete,还有在PostNcDestroy的自动清理执行过程中调用delete this)。

After calling DestroyWindow on a non-auto-cleanup object, the C++ object will still be around, but m_hWnd will be NULL. After calling DestroyWindow on an auto-cleanup object, the C++ object will be gone, freed by the C++ delete operator in the auto-cleanup implementation of PostNcDestroy.

对于一个不执行自动清理的对象,在调用DestroyWindow之后,这个C++对象仍然存在,但是m_hWnd会为空。对一个执行自动清理的对象,在调用DestroyWindow之后,此C++对象就不存在了,它被PostNcDestroy的自动清理执行过程里的delete操作符释放。

[参考资料]

http://hi.baidu.com/busycai/blog/item/fe57e41ec0b66b1e40341727.html

http://blog.csdn.net/wsxz_wsxz/archive/2010/03/25/5416732.aspx

阅读(2719) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~