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

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

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

задачи;

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

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

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

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

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

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

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

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

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

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

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

Примечание.

Система обычно вызывает CWnd::PostNcDestroy сообщение Windows WM_NCDESTROY и HWND объект окна C++ больше не подключен. Система также вызывается 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++ или глобальный DestroyWindow API.

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

Для объектов окна C++, которые не выполняют автоматическую очистку, оператор delete может вызвать утечку памяти при попытке вызвать DestroyWindow деструктор, если VTBL он не указывает на правильный CWnd::~CWnd производный класс. Утечка возникает, так как система не может найти соответствующий метод уничтожения для вызова. Использование 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.

См. также

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