TN017: Destroying Window Objects
TN017: Destroying Window Objects
이 노트는 CWnd::PostNcDestory 멤버 함수의 사용을 설명한다. 만일 CWnd를 상속한 객체에 최적화된 할당을 만들려고 한다면 이 함수를 사용해라. 이 노트는 또한 아주 중요한 규칙에 대한 몇 가지 이유를 설명한다. C++ 윈도우 객체를 파괴(destroy)하기 위해서는, “delete” 하지 마라. 이것은 중요하다. 만약 아래의 가이드라인을 따른다면, Cleanup 문제를 거의 가지지 않을 것이다. (예를 들면, C++ 메모리를 delete/free 하는 것을 잊어먹거나, HWND 와 같은 시스템 리소스를 해제하는 것을 잊어먹거나, 너무 많이 객체를 해제한다거나)
The Problem
윈도우 객체(CWnd를 상속한 클래스의 객체)는 C++ 객체(애플리케이션의 힙에 할당된) 와 HWND(윈도우 관리자에 의해 시스템 리소스 내에 할당된) 모두를 나타낸다. 윈도우 객체를 파괴하는 방법은 여러 가지가 있기 때문에, 시스템 리소스 또는 애플리케이션 메모리 누수를 방지하는 그리고 객체와 윈도우 핸들이 한번 이상 파괴되는 것을 방지하기 위한 어떤 규칙 세트를 제공해야만 한다.
이것은 메모리 관리 문제 보다 더 심각하다. 윈도우는 사용자-인터페이스를 가진다 : 화면에 그려지는 윈도우; 윈도우가 파괴되면, 시스템 리소스에도 영향이 있다. 애플리케이션 주소 공간 내에서 C++ 메모리 누수는 시스템 리소스가 누수 되는 것 만큼은 나쁘지 않다.
Destroying Windows
윈도우 객체를 파괴하는 두 가지 가능한 방법은 :
- CWnd::DestoryWindow 를 호출하거나 윈도우 API ::DestoryWindow를 호출한다.
- Delete 연산자를 사용하여 명시적으로 삭제 하기
첫 번째 경우는 지금까지 가장 일반적이다. 이 경우는 당신의 코드가 직접적으로
DestoryWindow를 호출하지 않더라도 적용된다. 사용자가 직접적으로 프레임 윈도우를 닫을 때(WM_CLOSE의 기본적인 행위는 DestoryWindow를 호출하는 것이다), 그리고 부모 윈도우가 파괴 될 때, 윈도우는 모든 자식에 대해 DestoryWindow를 호출한다.
두 번째 경우, 윈도우 객체에 대한 delete 연산자의 사용, 는 거의 사용되어서는 안되며 아래 소개된 경우에만 사용되어야 한다. (The second case, the use of the delete operator on Windows objects, should be very rare and only in the cases outlined below.)
Auto Cleanup with CWnd::PostNcDestroy
윈도우를 파괴할 때, 윈도우로 보내지는 마지막 윈도우 메시지는 WM_NCDESTORY 이다. 그 메시지에 대한 디폴트 CWnd 핸들러(CWnd::OnNcDestory)는 C++ 개체와 HWND를 분리할 것이다 그리고 가상 함수인 PostNcDestory를 호출할 것이다. 일부 클래스는 C++ 객체를 삭제하기 위해 이 함수를 재정의(override)한다.
CWnd::PostNcDestory의 디폴트 구현은 아무것도 하지 않는다 그것은 스택 프레임에 할당된 윈도우 객체 또는 다른 객체 내에 내장된(embedded) 윈도우 객체에 대해서는 적절하다. 힙에 할당되도록 디자인 된 (다른 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).
힙에 할당되도록 디자인 된 클래스는 “delete this”를 수행하기 위해 PostNcDestory 멤버 함수를 재정의 한다. 이 문장은 C++ 객체와 연결된 모든 C++ 메모리를 해제할 것이다. 만일 m_hWnd 가 NULL 이 아닌 경우 디폴트 CWnd 소멸자가 DestoryWindow를 호출하더라도, 핸들은 분리될 것이고, Cleanup 단계(phase)동안 NULL이 되기 때문에 이것은 무한 재귀(infinite recursion)로 빠지지는 않는다.
Note CWnd::PostNcDestroy은 일반적으로 윈도우 파괴의 일부로서, WM_NCDESTORY 메시지가 처리된 후에 호출된다, 그리고 HWND 와 C++ 윈도우 객체는 더 이상 붙여지지(attached) 않는다. CWnd::PostNcDestory 는 실패(failure)가 일어나면 대부분의 Create 호출의 구현 내에서 또한 호출될 것이다. (자동 Cleanup 규칙에 대해서는 아래를 보라)
Auto Cleanup Classes
아래 클래스는 auto-cleanup을 위해 디자인되지 않았다. 일반적으로 다른 C++ 객체 내에 내장되거나 스택 상에 존재한다:
- All standard Windows controls (CStatic, CEdit, CListBox, and so on).
- Any child windows derived directly from CWnd (for example, custom controls).
- Splitter windows (CSplitterWnd).
- Default control bars (classes derived from CControlBar, see Technical Note 31 for enabling auto-delete for control bar objects).
- Dialogs (CDialog) designed for modal dialogs on the stack frame.
- All the standard dialogs except CFindReplaceDialog.
- The default dialogs created by ClassWizard.
아래 클래스는 auto-cleanup을 위해 디자인되어 있다. 일반적으로 힙에 할당된다.
- Main frame windows (derived directly or indirectly from CFrameWnd).
- View windows (derived directly or indirectly from CView).
만일 이러한 모든 규칙을 깨뜨리고 싶다면, 상속한 클래스 안에 PostNcDestory 멤버 함수를 재정의 해야만 한다. 클래스에 auto-cleanup을 추가하기 위해서는, 기본 클래스를 호출하고 그러고 나서 delete this 를 수행한다. Auto-cleanup을 클래스로부터 제거하기 위해서는, 기본 클래스내에 PostNcDestroy 함수 대신에 바로 CWnd::PonstNcDestroy 를 호출한다.
위에서 언급한 것에 대한 가장 일반적인 사용은 힙에 할당될 수 있는 modeless 다이얼로그를 생성하는 것이다.
When to Call 'delete'
윈도우 객체를 파괴하기 위한 권장되는 방법은 C++ 멤버 함수 또는 전역 ::DestroyWindow API 둘 중 하나, DestroyWindow를 호출하는 것이다.
MDI 자식 윈도우를 파괴하기 위해서는 전역 ::DestroyWindow API를 호출해서는 안된다. 대신에 가상 멤버 함수 CWnd::DestroyWindow 를 사용한다.
Auto-cleanup을 수행하지 않는 C++ 윈도우 객체의 경우, delete 대신에 DestroyWindow 를 사용하는 것은 CWnd::~CWnd 소멸자 내에서 VTBL 이 올바르게 상속된 클래스를 가리키고 있지 않는 DestoryWindow를 호출해야만 하는 문제를 피하게 한다. MFC의 진단 (디버그) 버전이 경고할 미묘한 버그가 이 경우에 만들어 질 수 있다.
Warning: calling DestroyWindow in CWnd::~CWnd
OnDestroy or PostNcDestroy in derived class will not be called
Auto-cleanup을 수행하는 C++ 윈도우 객체의 경우, 반드시 DestroyWindow를 호출해야만 한다. 만약 delete 연산자를 직접적으로 사용한다면, MFC 진단 메모리 할당자(MFC diagnostic memory allocator)는 당신이 메모리를 두 번 해제하고 있다고 알려 줄 것이다 (PostNcDestroy의 auto-cleanup 구현 내에서 delete에 대한 첫 번째 호출뿐만 아니라 “delete this”에 의한 간접적인 호출).
출처 : MSDN 2003 (ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/vclib/html/_MFCNOTES_TN017.htm)