TN017: Destruindo objetos de janela
Esta observação descreve o uso do método CWnd::PostNcDestroy
. Use esse método se quiser fazer a alocação personalizada de objetos derivados de CWnd
. Esta observação também explica por que você deve usar CWnd::DestroyWindow
para destruir um objeto C++ do Windows em vez do operador delete
.
Se você seguir as diretrizes neste artigo, terá alguns problemas de limpeza. Esses problemas podem resultar de problemas como esquecer de excluir/liberar memória C++, esquecer de liberar recursos do sistema, como HWND
s, ou liberar objetos muitas vezes.
O problema
Cada objeto de janela (objeto de uma classe derivada de CWnd
) representa um objeto C++ e um HWND
. Os objetos C++ são alocados no heap do aplicativo e HWND
s são alocados em recursos do sistema pelo gerenciador de janelas. Como há várias maneiras de destruir um objeto de janela, precisamos fornecer um conjunto de regras que impeçam vazamentos de recursos ou memória do sistema. Essas regras também precisam impedir que objetos e identificadores do Windows sejam destruídos mais de uma vez.
Destruindo janelas
Veja as seguintes duas maneiras permitidas de destruir um objeto do Windows:
Chamar
CWnd::DestroyWindow
ou a API do WindowsDestroyWindow
.Excluir explicitamente com o operador
delete
.
O primeiro caso é de longe o mais comum. Esse caso se aplica mesmo que seu código não chame DestroyWindow
diretamente. Quando o usuário fecha diretamente uma janela do quadro, essa ação gera a mensagem WM_CLOSE e a resposta padrão para essa mensagem é chamar DestroyWindow
. Quando uma janela pai é destruída, o Windows chama DestroyWindow
para todos os filhos dela.
O segundo caso, o uso do operador delete
em objetos do Windows, deve ser raro. Veja a seguir alguns casos em que usar delete
é a opção correta.
Limpeza automática com CWnd::PostNcDestroy
Quando o sistema destrói uma janela do Windows, a última mensagem do Windows enviada para a janela é WM_NCDESTROY
. O manipulador padrão CWnd
dessa mensagem é CWnd::OnNcDestroy
. OnNcDestroy
desanexará o HWND
do objeto C++ e chamará a função virtual PostNcDestroy
. Algumas classes substituem essa função para excluir o objeto C++.
A implementação padrão de CWnd::PostNcDestroy
não faz nada, o que é apropriado para objetos de janela alocados no registro de ativação ou inseridos em outros objetos. Esse comportamento não é apropriado para objetos de janela projetados para alocação no heap sem outros objetos. Em outras palavras, não é apropriado para objetos de janela que não estão inseridos em outros objetos C++.
Classes projetadas apenas para alocação no heap substituem o método PostNcDestroy
para executar um delete this;
. Essa instrução liberará qualquer memória associada ao objeto C++. Embora o destruidor padrão CWnd
chame DestroyWindow
se m_hWnd
não for NULL
, essa chamada não levará à recursão infinita porque o identificador será desanexado e NULL
durante a fase de limpeza.
Observação
O sistema geralmente chama CWnd::PostNcDestroy
depois que processa a mensagem WM_NCDESTROY
do Windows, e o HWND
e o objeto C++ do Windows não estão mais conectados. O sistema também chamará CWnd::PostNcDestroy
na implementação da maioria das chamadas CWnd::Create
se ocorrer falha. As regras de limpeza automática são descritas posteriormente neste artigo.
Classes de limpeza automática
As classes a seguir não são projetadas para limpeza automática. Normalmente, elas são inseridas em outros objetos C++ ou na pilha:
Todos os controles padrão do Windows (
CStatic
,CEdit
,CListBox
e assim por diante).Todas as janelas filho derivadas diretamente de
CWnd
(por exemplo, controles personalizados).Janelas divisoras (
CSplitterWnd
).Barras de controle padrão (classes derivadas de
CControlBar
, confira a Nota Técnica 31 para habilitar a exclusão automática para objetos da barra de controle).Caixas de diálogo (
CDialog
) projetadas para caixas de diálogo modais no registro de ativação.Todas as caixas de diálogo padrão, exceto
CFindReplaceDialog
.As caixas de diálogo padrão criadas por ClassWizard.
As classes a seguir são projetadas para limpeza automática. Eles normalmente são alocados por si mesmos no heap:
Janelas de quadro principal (derivadas direta ou indiretamente de
CFrameWnd
).Exibir janelas (derivadas direta ou indiretamente de
CView
).
Se você quiser quebrar essas regras, precisará substituir o método PostNcDestroy
em sua classe derivada. Para adicionar a limpeza automática à sua classe, chame sua classe base e faça um delete this;
. Para remover a limpeza automática de sua classe, chame CWnd::PostNcDestroy
diretamente em vez do método PostNcDestroy
de sua classe base direta.
O uso mais comum da alteração do comportamento de limpeza automática é criar uma caixa de diálogo sem janela restrita que possa ser alocada no heap.
Quando chamar delete
Recomendamos que você chame DestroyWindow
para destruir um objeto do Windows, o método C++ ou a API global DestroyWindow
.
Não chame a API global DestroyWindow
para destruir uma janela filho do MDI. Você deve usar o método virtual CWnd::DestroyWindow
em vez disso.
Para objetos da Janela C++ que não executam a limpeza automática, o uso do operador delete
pode causar um vazamento de memória quando você tenta chamar DestroyWindow
no destruidor CWnd::~CWnd
se VTBL
não apontar para a classe derivada corretamente. O vazamento ocorre porque o sistema não pode encontrar o método de destruição apropriado para chamar. Usar DestroyWindow
em vez de delete
evita esses problemas. Como esse erro pode ser sutil, a compilação no modo de depuração gerará o aviso a seguir se você estiver em risco.
Warning: calling DestroyWindow in CWnd::~CWnd
OnDestroy or PostNcDestroy in derived class will not be called
Para objetos do Windows C++ que executam a limpeza automática, você precisa chamar DestroyWindow
. Se você usar o operador delete
diretamente, o alocador de memória de diagnóstico MFC notificará você de que você está liberando memória duas vezes. As duas ocorrências são a sua primeira chamada explícita e a chamada indireta para delete this;
na implementação de limpeza automática de PostNcDestroy
.
Depois que você chamar DestroyWindow
em um objeto de limpeza não automática, o objeto C++ ainda estará por perto, mas m_hWnd
será NULL
. Depois que você chamar DestroyWindow
em um objeto de limpeza automática, o objeto C++ será apagado, liberado pelo operador de exclusão C++ na implementação de limpeza automática de PostNcDestroy
.
Confira também
Observações técnicas por número
Observações técnicas por categoria