Поделиться через


TN017: уничтожение объектов окна

В этом примечании описывается использование CWnd::PostNcDestroy метода. Используйте этот метод, если вы хотите выполнить настраиваемое выделение производных CWndобъектов. В этом примечании также объясняется, почему следует использовать CWnd::DestroyWindow для уничтожения объекта Windows C++ вместо delete оператора.

Если вы следуйте рекомендациям, описанным в этой статье, у вас будет мало проблем с очисткой. Эти проблемы могут привести к таким проблемам, как забыв удалить или освободить память C++, забыв освободить системные ресурсы, такие как HWNDs, или освободить объекты слишком много раз.

задачи;

Каждый объект Windows (объект класса, производный от CWnd) представляет как объект C++, так и объект HWND. Объекты C++ выделяются в куче приложения, а элементы HWND выделяются в системных ресурсах менеджером окон. Так как существует несколько способов уничтожения объекта окна, необходимо предоставить набор правил, которые препятствуют утечке системного ресурса или памяти. Эти правила также должны предотвращать уничтожение объектов и дескрипторов Windows более одного раза.

Уничтожение окон

Ниже приведены два разрешенных способа уничтожения объекта Windows:

  • Вызов CWnd::DestroyWindow или Windows API DestroyWindow.

  • Явное удаление с помощью оператора delete.

Первый случай является самым распространенным. Этот случай действует, даже если ваш код напрямую не обращается к DestroyWindow. Когда пользователь напрямую закрывает оконное окно, это действие создает сообщение WM_CLOSE, и ответ по умолчанию на это сообщение состоит в вызове DestroyWindow. При уничтожении родительского окна Windows вызывает DestroyWindow для всех его дочерних элементов.

Во втором случае использование оператора delete в объектах Windows должно быть редким. Ниже приведены некоторые случаи, когда использование delete является правильным выбором.

Автоматическая очистка с помощью CWnd::PostNcDestroy

Когда система уничтожает окно Windows, последнее сообщение Windows, отправленное в окно, будет WM_NCDESTROY. Обработчик по умолчанию для этого сообщения — CWndCWnd::OnNcDestroy. OnNcDestroy отсоединит HWND от объекта C++ и вызовет виртуальную функцию PostNcDestroy. Некоторые классы переопределяют эту функцию, чтобы удалить объект C++.

Реализация по умолчанию CWnd::PostNcDestroy не делает ничего, что подходит для объектов окна, размещённых в кадре стека или встроенных в другие объекты. Это поведение не подходит для объектов окон, предназначенных для выделения в куче без других объектов. Другими словами, он не подходит для объектов окон, которые не внедрены в другие объекты C++.

Классы, предназначенные для выделения только в куче, переопределяют PostNcDestroy метод для выполнения delete this;. Эта инструкция освобождает любую память, связанную с объектом C++. Несмотря на то что деструктор по умолчанию CWnd вызывает DestroyWindow если m_hWnd не является NULL, этот вызов не приводит к бесконечной рекурсии, так как дескриптор будет отсоединен и NULL во время этапа очистки.

Замечание

Система обычно вызывает CWnd::PostNcDestroy после обработки сообщения Windows WM_NCDESTROY, и объект окна C++ HWND больше не подключен. Система также будет вызывать CWnd::PostNcDestroy в процессе реализации большинства вызовов CWnd::Create, в случае сбоя. Правила автоматической очистки описаны далее в этой статье.

Классы автоматической очистки

Следующие классы не предназначены для автоматической очистки. Обычно они внедрены в другие объекты C++ или на стеке:

  • Все стандартные элементы управления Windows (CStatic, CEditи CListBoxт. д.).

  • Все дочерние окна, производные непосредственно от CWnd (например, пользовательских элементов управления).

  • Разделитель окон (CSplitterWnd).

  • Панели управления по умолчанию (классы, производные от CControlBar, см. Техническая записка 31 для включения автоматического удаления объектов панелей управления).

  • Диалоги (CDialog) предназначены для модальных диалогов в кадре стека.

  • Все стандартные диалоги, кроме CFindReplaceDialog.

  • Диалоги по умолчанию, созданные ClassWizard.

Следующие классы предназначены для автоматической очистки. Они обычно выделяются сами по себе в куче:

  • Окна главной рамы (производные напрямую или косвенно от CFrameWnd).

  • Просмотр окон (производных напрямую или косвенно от CView).

Если вы хотите нарушить эти правила, необходимо переопределить PostNcDestroy метод в производном классе. Чтобы добавить автоматическую очистку в класс, вызовите базовый класс и выполните команду delete this;. Чтобы удалить автоматическую очистку из класса, вызовите CWnd::PostNcDestroy непосредственно вместо PostNcDestroy метода прямого базового класса.

Наиболее распространенным способом изменения поведения автоматической очистки памяти является создание модельного диалогового окна, которое можно выделить в куче.

Когда следует вызывать delete

Рекомендуем вызвать DestroyWindow, чтобы уничтожить объект Windows, используя метод C++ или глобальный API DestroyWindow.

Не вызывайте глобальный API DestroyWindow для удаления дочернего окна MDI. Вместо этого следует использовать виртуальный метод CWnd::DestroyWindow .

Для объектов окна C++, которые не выполняют автоматическую очистку, оператор delete может вызвать утечку памяти при попытке вызвать DestroyWindow в деструкторе CWnd::~CWnd, если VTBL не указывает на правильный производный класс. Утечка возникает, так как система не может найти соответствующий метод уничтожения для вызова. Использование DestroyWindow вместо delete позволяет избежать этих проблем. Поскольку эта ошибка может быть незаметной, компиляция в режиме отладки сгенерирует следующее предупреждение, если существует риск.

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

Для объектов Windows C++, выполняющих автоматическую очистку, необходимо вызвать DestroyWindow. Если вы используете оператор delete напрямую, диагностическое средство выделения памяти MFC сообщит вам о том, что вы освобождаете память дважды. Две операции — это ваш первый явный вызов и косвенный вызов delete this; в реализации автоочистки PostNcDestroy.

После вызова DestroyWindow для объекта, отличного от автоматической очистки, объект C++ по-прежнему будет существовать, но m_hWnd станет NULL. После вызова DestroyWindow на объекте с автоматической очисткой, объект C++ будет удален оператором удаления C++ в реализации автоматической очистки PostNcDestroy.

См. также

Технические заметки по номеру
Технические заметки по категориям