TN017: Eliminazione di oggetti finestra

Questa nota descrive l'uso del CWnd::PostNcDestroy metodo . Utilizzare questo metodo se si desidera eseguire un'allocazione personalizzata di CWndoggetti derivati da . Questa nota spiega anche perché è consigliabile usare CWnd::DestroyWindow per eliminare definitivamente un oggetto Windows C++ anziché l'operatore delete .

Se si seguono le linee guida in questo articolo, si avranno alcuni problemi di pulizia. Questi problemi possono derivare da problemi come dimenticare di eliminare/liberare memoria C++, dimenticando di liberare risorse di sistema come HWNDs o liberando oggetti troppe volte.

Problema

Ogni oggetto windows (oggetto di una classe derivata da CWnd) rappresenta sia un oggetto C++ che un oggetto HWND. Gli oggetti C++ vengono allocati nell'heap dell'applicazione e HWNDs vengono allocati nelle risorse di sistema dal gestore finestre. Poiché esistono diversi modi per eliminare definitivamente un oggetto finestra, è necessario fornire un set di regole che impediscono perdite di memoria o risorse di sistema. Queste regole devono inoltre impedire che gli oggetti e gli handle di Windows vengano eliminati definitivamente più di una volta.

Eliminazione di finestre

Di seguito sono riportati i due modi consentiti per distruggere un oggetto Windows:

  • Chiamata CWnd::DestroyWindow o api DestroyWindowdi Windows.

  • Eliminazione esplicita con l'operatore delete .

Il primo caso è di gran lunga il più comune. Questo caso si applica anche se il codice non chiama DestroyWindow direttamente. Quando l'utente chiude direttamente una finestra cornice, questa azione genera il messaggio WM_CLOedizione Standard e la risposta predefinita a questo messaggio consiste nel chiamare DestroyWindow. Quando una finestra padre viene eliminata definitivamente, Windows chiama DestroyWindow tutti i relativi elementi figlio.

Il secondo caso, l'uso dell'operatore delete negli oggetti Windows deve essere raro. Di seguito sono riportati alcuni casi in cui l'uso delete è la scelta corretta.

Pulizia automatica con CWnd::PostNcDestroy

Quando il sistema elimina definitivamente una finestra di Windows, l'ultimo messaggio di Windows inviato alla finestra è WM_NCDESTROY. Il gestore predefinito CWnd per il messaggio è CWnd::OnNcDestroy. OnNcDestroy scollegare l'oggetto HWND dall'oggetto C++ e chiamare la funzione PostNcDestroyvirtuale . Alcune classi eseguono l'override di questa funzione per eliminare l'oggetto C++.

L'implementazione predefinita di CWnd::PostNcDestroy non esegue alcuna operazione, appropriata per gli oggetti finestra allocati nello stack frame o incorporati in altri oggetti. Questo comportamento non è appropriato per gli oggetti finestra progettati per l'allocazione nell'heap senza altri oggetti. In altre parole, non è appropriato per gli oggetti finestra che non sono incorporati in altri oggetti C++.

Le classi progettate per l'allocazione da sole nell'heap eseguono l'override del PostNcDestroy metodo per eseguire un oggetto delete this;. Questa istruzione libera qualsiasi memoria associata all'oggetto C++. Anche se il distruttore predefinito CWnd chiama DestroyWindow se m_hWnd non NULLè , questa chiamata non comporta una ricorsione infinita perché l'handle verrà scollegato e NULL durante la fase di pulizia.

Nota

Il sistema chiama CWnd::PostNcDestroy in genere dopo l'elaborazione del messaggio di Windows WM_NCDESTROY e l'oggetto HWND finestra C++ non sono più connessi. Il sistema chiamerà CWnd::PostNcDestroy anche nell'implementazione della maggior parte CWnd::Create delle chiamate in caso di errore. Le regole di pulizia automatica sono descritte più avanti in questo articolo.

Classi di pulizia automatica

Le classi seguenti non sono progettate per la pulizia automatica. In genere sono incorporati in altri oggetti C++ o nello stack:

  • Tutti i controlli Windows standard (CStatic, CEdit, CListBoxe così via).

  • Qualsiasi finestra figlio derivata direttamente da CWnd (ad esempio, controlli personalizzati).

  • Finestre splitter (CSplitterWnd).

  • Barre di controllo predefinite (classi derivate da CControlBar, vedere La nota tecnica 31 per abilitare l'eliminazione automatica per gli oggetti barra di controllo).

  • Dialoghi (CDialog) progettati per i dialoghi modali nel frame dello stack.

  • Tutti i dialoghi standard, ad eccezione CFindReplaceDialogdi .

  • Finestre di dialogo predefinite create da ClassWizard.

Le classi seguenti sono progettate per la pulizia automatica. In genere vengono allocati da soli nell'heap:

  • Finestre cornice principale (derivate direttamente o indirettamente da CFrameWnd).

  • Visualizzare le finestre (derivate direttamente o indirettamente da CView).

Se si desidera interrompere queste regole, è necessario eseguire l'override del PostNcDestroy metodo nella classe derivata. Per aggiungere la pulizia automatica alla classe, chiamare la classe di base e quindi eseguire un oggetto delete this;. Per rimuovere la pulizia automatica dalla classe, chiamare CWnd::PostNcDestroy direttamente anziché il PostNcDestroy metodo della classe base diretta.

L'uso più comune della modifica del comportamento di pulizia automatica consiste nel creare una finestra di dialogo senza modalità che può essere allocata nell'heap.

Quando chiamare delete

È consigliabile chiamare DestroyWindow per eliminare definitivamente un oggetto Windows, ovvero il metodo C++ o l'API globale DestroyWindow .

Non chiamare l'API globale DestroyWindow per eliminare definitivamente una finestra figlio MDI. È invece consigliabile usare il metodo CWnd::DestroyWindow virtuale.

Per gli oggetti Window C++ che non eseguono la pulizia automatica, l'uso dell'operatore delete può causare una perdita di memoria quando si tenta di chiamare DestroyWindow nel CWnd::~CWnd distruttore se non VTBL punta alla classe derivata correttamente. La perdita si verifica perché il sistema non riesce a trovare il metodo di eliminazione eliminato appropriato da chiamare. L'uso DestroyWindow di invece di delete evitare questi problemi. Poiché questo errore può essere sottile, la compilazione in modalità di debug genererà l'avviso seguente se si è a rischio.

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

Per gli oggetti Windows C++ che eseguono la pulizia automatica, è necessario chiamare DestroyWindow. Se si usa direttamente l'operatore, l'allocatore delete di memoria diagnostica MFC informerà che si sta liberando la memoria due volte. Le due occorrenze sono la prima chiamata esplicita e la chiamata indiretta a delete this; nell'implementazione della pulizia automatica di PostNcDestroy.

Dopo aver chiamato DestroyWindow su un oggetto non di pulizia automatica, l'oggetto C++ sarà ancora in giro, ma m_hWnd sarà NULL. Dopo aver chiamato DestroyWindow su un oggetto di pulizia automatica, l'oggetto C++ non sarà più disponibile, liberato dall'operatore di eliminazione C++ nell'implementazione della pulizia automatica di PostNcDestroy.

Vedi anche

Note tecniche per numero
Note tecniche per categoria