Compartilhar via


TN017: Destruindo objetos de janela

Esta observação descreve o uso do CWnd::PostNcDestroy método. Use esse método se você quiser fazer a alocação personalizada de CWndobjetos derivados. Esta observação também explica por que você deve usar CWnd::DestroyWindow para destruir um objeto C++ do Windows em vez do delete operador.

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 HWNDs, ou liberar objetos muitas vezes.

O problema

Cada objeto windows (objeto de uma classe derivada CWnd) representa um objeto C++ e um HWND. Os objetos C++ são alocados no heap do aplicativo e HWNDsão alocados em recursos do sistema pelo gerenciador de janelas. Como há várias maneiras de destruir um objeto de janela, devemos fornecer um conjunto de regras que impeçam vazamentos de recursos ou memória do sistema. Essas regras também devem impedir que objetos e identificadores do Windows sejam destruídos mais de uma vez.

Destruindo janelas

Veja a seguir as duas maneiras permitidas de destruir um objeto do Windows:

  • Chamada CWnd::DestroyWindow ou a API DestroyWindowdo Windows.

  • Excluindo explicitamente com o delete operador.

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 de quadros, 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 todos os seus filhos.

O segundo caso, o uso do delete operador em objetos windows, deve ser raro. Veja a seguir alguns casos em que usar delete é a escolha 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 objeto C++ e chamará a função PostNcDestroyvirtual. Algumas classes substituem essa função para excluir o objeto C++.

A implementação padrão não CWnd::PostNcDestroy faz nada, o que é apropriado para objetos de janela que são alocados no quadro de pilha 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 PostNcDestroy método para executar um delete this;. Essa instrução liberará qualquer memória associada ao objeto C++. Mesmo que o destruidor padrão CWnd chame DestroyWindow se m_hWnd não NULLestiver, 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 do Windows WM_NCDESTROY e o HWND objeto de janela C++ não estão mais conectados. O sistema também chamará CWnd::PostNcDestroy a implementação da maioria CWnd::Create das chamadas 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, eles são inseridos em outros objetos C++ ou na pilha:

  • Todos os controles padrão do Windows (CStatice CEditCListBoxassim por diante).

  • Qualquer janela filho derivada diretamente de CWnd (por exemplo, controles personalizados).

  • Janelas de divisão (CSplitterWnd).

  • Barras de controle padrão (classes derivadas de CControlBar, consulte a Observação Técnica 31 para habilitar a exclusão automática para objetos da barra de controle).

  • Caixas de diálogo (CDialog) projetadas para diálogos modais no quadro de pilha.

  • Todas as caixas de diálogo padrão, exceto CFindReplaceDialog.

  • As caixas de diálogo padrão criadas pelo 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, deverá substituir o PostNcDestroy método 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 PostNcDestroy método de sua classe base direta.

O uso mais comum da alteração do comportamento de limpeza automática é criar uma caixa de diálogo modeless que pode ser alocada no heap.

Quando chamar delete

Recomendamos que você chame DestroyWindow para destruir um objeto windows, o método C++ ou a API global DestroyWindow .

Não chame a API global DestroyWindow para destruir uma janela MDI Child. Em vez disso, você deve usar o método CWnd::DestroyWindow virtual.

Para objetos de janela C++ que não executam a limpeza automática, o uso do delete operador pode causar um vazamento de memória quando você tenta chamar DestroyWindow o CWnd::~CWnd destruidor se não VTBL apontar para a classe derivada corretamente. O vazamento ocorre porque o sistema não consegue encontrar o método de destruição apropriado para chamar. Usar DestroyWindow em vez de delete evitar 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 C++ do Windows que executam a limpeza automática, você deve chamar DestroyWindow. Se você usar o delete operador 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 sua primeira chamada explícita e a chamada indireta na delete this; implementação de limpeza automática de PostNcDestroy.

Depois de chamar DestroyWindow 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 um objeto de limpeza automática, o objeto C++ desaparecerá, liberado pelo operador de exclusão C++ na implementação de limpeza automática de PostNcDestroy.

Consulte também

Notas técnicas por número
Notas técnicas por categoria