Udostępnij przez


TN017: Niszczenie obiektów okien

Ta uwaga opisuje użycie CWnd::PostNcDestroy metody . Użyj tej metody, jeśli chcesz dostosować alokację CWndobiektów pochodnych. W tej notatce wyjaśniono również, dlaczego należy użyć CWnd::DestroyWindow polecenia w celu zniszczenia obiektu systemu Windows w języku C++ zamiast delete operatora.

Jeśli zastosujesz się do wytycznych opisanych w tym artykule, nie będziesz mieć problemów z czyszczeniem. Te problemy mogą wynikać z problemów, takich jak zapominanie o usuwaniu/zwalnianiu pamięci C++, zapominaniu o zwalnianiu zasobów systemowych, takich jak HWNDs, lub zwalnianiu obiektów zbyt wiele razy.

Problem

Każdy obiekt systemu Windows (obiekt klasy pochodnej od CWnd) reprezentuje zarówno obiekt C++, jak i HWND. Obiekty języka C++ są przydzielane w stercie aplikacji i HWNDsą przydzielane w zasobach systemowych przez menedżera okien. Ponieważ istnieje kilka sposobów zniszczenia obiektu okna, musimy podać zestaw reguł, które uniemożliwiają przecieki zasobów systemowych lub pamięci. Te zasady muszą również uniemożliwić niszczenie obiektów i uchwytów systemu Windows więcej niż raz.

Niszczenie okien

Poniżej przedstawiono dwa dozwolone sposoby zniszczenia obiektu systemu Windows:

  • Wywołanie CWnd::DestroyWindow lub interfejsu API systemu Windows DestroyWindow.

  • Jawne usuwanie za pomocą delete operatora .

Pierwszy przypadek jest zdecydowanie najbardziej typowy. Ten przypadek ma zastosowanie nawet wtedy, gdy kod nie wywołuje DestroyWindow bezpośrednio. Gdy użytkownik zamyka okno ramki bezpośrednio, ta akcja generuje komunikat WM_CLOSE, a domyślną odpowiedzią na ten komunikat jest wywołanie metody DestroyWindow. Kiedy okno nadrzędne zostaje zniszczone, Windows uruchamia DestroyWindow dla wszystkich jego elementów podrzędnych.

Drugi przypadek, użycie delete operatora w obiektach systemu Windows, powinno być rzadkie. Poniżej przedstawiono niektóre przypadki, w których użycie delete jest właściwym wyborem.

Automatyczne czyszczenie przy użyciu CWnd::PostNcDestroy

Gdy system zniszczy okno systemu Windows, ostatnim komunikatem systemu Windows wysłanym do okna jest WM_NCDESTROY. Domyślną CWnd procedurą obsługi tego komunikatu jest CWnd::OnNcDestroy. OnNcDestroy spowoduje odłączenie HWND obiektu C++ i wywoła funkcję PostNcDestroywirtualną . Niektóre klasy zastępują tę funkcję, aby usunąć obiekt C++.

Domyślna implementacja CWnd::PostNcDestroy nie wykonuje żadnych operacji, co jest odpowiednie dla obiektów okien przydzielonych na ramce stosu lub osadzonych w innych obiektach. To zachowanie nie jest odpowiednie dla obiektów okien przeznaczonych do alokacji na stercie bez żadnych innych obiektów. Innymi słowy, nie jest to odpowiednie dla obiektów okien, które nie są osadzone w innych obiektach języka C++.

Klasy przeznaczone tylko do alokacji na stercie przesłaniają metodę PostNcDestroy w celu realizacji delete this;. Ta instrukcja zwolni wszelkie pamięci skojarzone z obiektem C++. Mimo że domyślny destruktor CWnd wywołuje DestroyWindow, jeśli m_hWnd nie jest NULL, to wywołanie nie prowadzi do nieskończonej rekursji, ponieważ uchwyt zostanie odłączony i NULL w fazie oczyszczania.

Uwaga / Notatka

System zwykle wywołuje CWnd::PostNcDestroy po przetwarzaniu komunikatu systemu Windows WM_NCDESTROY , a HWND obiekt okna C++ nie jest już połączony. System wywoła również CWnd::PostNcDestroy w implementacji większości CWnd::Create wywołań, jeśli wystąpi awaria. Reguły automatycznego czyszczenia zostały opisane w dalszej części tego artykułu.

Klasy automatycznego czyszczenia

Następujące klasy nie są przeznaczone do automatycznego czyszczenia. Są one zwykle osadzone w innych obiektach języka C++ lub na stosie:

  • Wszystkie standardowe kontrolki systemu Windows (CStatic, CEdit, CListBoxi tak dalej).

  • Wszystkie okna podrzędne pochodzące bezpośrednio z CWnd (na przykład kontrolki niestandardowe).

  • Okna dzielone (CSplitterWnd).

  • Domyślne paski sterowania (klasy wywodzące się z CControlBar — zobacz Technical Note 31 w celu włączenia automatycznego usuwania obiektów paska sterowania).

  • Okna dialogowe (CDialog) przeznaczone dla modalnych okien dialogowych na ramce.

  • Wszystkie standardowe okna dialogowe z wyjątkiem CFindReplaceDialog.

  • Domyślne okna dialogowe utworzone przez klasę ClassWizard.

Następujące klasy są przeznaczone do automatycznego czyszczenia. Są one zwykle przydzielane przez siebie na stercie:

  • Główne okna ramy (pochodzące bezpośrednio lub pośrednio z CFrameWnd).

  • Wyświetl okna (pochodzące bezpośrednio lub pośrednio z CView).

Jeśli chcesz obejść te zasady, musisz zastąpić metodę PostNcDestroy w klasie pochodnej. Aby dodać automatyczne czyszczenie do klasy, wywołaj klasę bazową, a następnie wykonaj polecenie delete this;. Aby usunąć automatyczne czyszczenie z klasy, wywołaj CWnd::PostNcDestroy bezpośrednio, zamiast metody PostNcDestroy w swojej bezpośredniej klasie bazowej.

Najczęstszym zastosowaniem zmiany zachowania automatycznego czyszczenia jest utworzenie bez modalnego okna dialogowego, które można przydzielić na stercie.

Kiedy należy zadzwonić delete

Zalecamy albo wywołanie metody C++, albo globalnego interfejsu DestroyWindow API w celu zniszczenia obiektu systemu Windows, korzystając z DestroyWindow.

Nie należy wywoływać globalnego DestroyWindow interfejsu API w celu zniszczenia okna podrzędnego MDI. Zamiast tego należy użyć metody CWnd::DestroyWindow wirtualnej.

W przypadku obiektów okna języka C++, które nie wykonują automatycznego czyszczenia, użycie delete operatora może spowodować przeciek pamięci podczas próby wywołania DestroyWindow w destruktorze CWnd::~CWnd, jeśli VTBL nie wskazuje na odpowiednio wyprowadzoną klasę. Wyciek występuje, ponieważ system nie może odnaleźć odpowiedniej metody niszczącej do wywołania. Używanie DestroyWindow zamiast delete unika tych problemów. Ponieważ ten błąd może być trudny do zauważenia, kompilowanie w trybie debugowania spowoduje wygenerowanie następującego ostrzeżenia, jeśli jesteś zagrożony.

Warning: calling DestroyWindow in CWnd::~CWnd
    OnDestroy or PostNcDestroy in derived class will not be called

W przypadku obiektów systemu Windows w języku C++, które wykonują automatyczne oczyszczanie, należy wywołać metodę DestroyWindow. Jeśli używasz operatora delete bezpośrednio, alokator pamięci diagnostycznej MFC powiadomi Cię, że dwukrotnie zwalniasz pamięć. Dwa wystąpienia to twoje pierwsze jawne wywołanie oraz pośrednie wywołanie delete this; w implementacji automatycznego oczyszczania modułu PostNcDestroy.

Po wywołaniu DestroyWindow na obiekcie bez automatycznego czyszczenia, obiekt C++ będzie nadal istnieć, ale m_hWnd będzie NULL. Po wywołaniu funkcji DestroyWindow na obiekcie automatycznego oczyszczania, obiekt C++ zostanie usunięty, zwolniony przez operator delete C++ w implementacji automatycznego oczyszczania PostNcDestroy.

Zobacz także

Uwagi techniczne według numeru
Uwagi techniczne według kategorii