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 CWnd
oggetti 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 HWND
s 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 HWND
s 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 apiDestroyWindow
di 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 PostNcDestroy
virtuale . 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
,CListBox
e 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
CFindReplaceDialog
di .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
.