Partilhar via


TN017: Destruindo objetos de janela

Esta nota descreve a utilização do CWnd::PostNcDestroy método. Use esse método se quiser fazer a alocação personalizada de CWndobjetos derivados. Esta nota 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 deste artigo, terá poucos 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 de Windows (objeto de uma classe derivada de CWnd) representa tanto um objeto C++ quanto um HWND. Os objetos C++ são alocados no heap da aplicação e HWNDs são alocados nos recursos de sistema pelo gestor 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

A seguir estão as duas maneiras permitidas de destruir um objeto do Windows:

  • Chamando CWnd::DestroyWindow ou a API DestroyWindow do Windows.

  • Exclusão explícita com o operador delete.

O primeiro caso é, de longe, o mais comum. Este caso aplica-se mesmo que o seu código não chame DestroyWindow diretamente. Quando o usuário fecha diretamente uma janela de quadro, essa ação gera a mensagem WM_CLOSE e a resposta padrão a essa mensagem é chamar DestroyWindow. Quando uma janela pai é destruída, o Windows chama DestroyWindow para todas as janelas filhas.

O segundo caso, o uso do operador delete em objetos Windows, deve ser raro. A seguir estão 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 para essa mensagem é CWnd::OnNcDestroy. OnNcDestroy desanexará o HWND do objeto C++ e chamará a função PostNcDestroyvirtual. 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 quadro de pilha ou incorporados em outros objetos. Esse comportamento não é apropriado para objetos de janela projetados para alocação na heap sem quaisquer outros objetos. Em outras palavras, não é apropriado para objetos de janela que não estão incorporados em outros objetos C++.

As classes projetadas apenas para alocação no heap substituem o método PostNcDestroy para realizar uma delete this;. Esta instrução liberará qualquer memória associada ao objeto C++. Ainda que o destruidor padrão CWnd chame DestroyWindow se m_hWnd não for NULL, essa chamada não resulta em recursão infinita porque o identificador será desvinculado e NULL durante a fase de limpeza.

Observação

O sistema geralmente chama CWnd::PostNcDestroy depois de processar a mensagem do Windows WM_NCDESTROY e o HWND e o objeto de janela C++ não estão mais conectados. O sistema também chamará CWnd::PostNcDestroy na implementação da maioria das chamadas de CWnd::Create se ocorrer falha. As regras de limpeza automática são descritas mais adiante neste artigo.

Classes de limpeza automática

As classes a seguir não foram projetadas para limpeza automática. Tipicamente, eles são incorporados em outros objetos C++ ou na pilha:

  • Todos os controles padrão do Windows (CStatic, CEdit, CListBox, e assim por diante).

  • Qualquer janela filha derivada diretamente de CWnd (por exemplo, controlos personalizados).

  • Janelas divisórias (CSplitterWnd).

  • Barras de controlo padrão (classes derivadas de CControlBar, consulte a Nota Técnica 31 para ativar a eliminação automática para objetos da barra de controlo).

  • Caixas de diálogo (CDialog) projetadas para diálogos modais na estrutura de pilha.

  • 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 sozinhos no heap.

  • Janelas de moldura principal (derivadas direta ou indiretamente de CFrameWnd).

  • Exibir janelas (derivadas direta ou indiretamente de CView).

Se quiser quebrar estas regras, deve sobrescrever o método PostNcDestroy na sua classe derivada. Para adicionar a limpeza automática à sua classe, chame a classe base e faça um delete this;. Para remover a limpeza automática da sua classe, chame diretamente CWnd::PostNcDestroy em vez do método PostNcDestroy da sua classe base direta.

O uso mais comum de alterar o comportamento de limpeza automática é criar uma caixa de diálogo sem modo que pode ser alocada no heap.

Quando ligar delete

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

Não chame a API global DestroyWindow para destruir uma janela MDI filha. Você deve usar o método CWnd::DestroyWindow virtual em vez disso.

Para objetos Window do C++ que não realizam limpeza automática, usar o operador delete pode causar um vazamento de memória quando tentar chamar DestroyWindow no destrutor CWnd::~CWnd se VTBL não apontar para a classe derivada correta. A fuga de memória ocorre porque o sistema não consegue encontrar o método adequado de eliminação a ser chamado. Usar DestroyWindow em vez de delete evitar esses problemas. Como esse erro pode ser sutil, compilar no modo de depuração gerará o seguinte aviso 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 limpeza automática, você deve chamar DestroyWindow. Se você usar o delete operador diretamente, o alocador de memória de diagnóstico MFC irá notificá-lo de que você está liberando memória duas vezes. As duas ocorrências são sua primeira chamada explícita e a chamada indireta para delete this; na implementação de limpeza automática do 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 de 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 do PostNcDestroy.

Ver também

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