TN016: Использование C++ множественного наследования с MFC

В настоящей записке описывается использование нескольких наследование (MI) с классов MFC.

Почему множественное наследование?

Над значением ми есть продолжается дискуссия в C++ и объектно ориентированных сообществ. Visual C++ компилятор и среду разработки полностью поддерживает ми.

Библиотека классов MFC был разработан таким образом, чтобы вам не нужно понимать ми использовать MFC. МИ не используется ни в одном из классов MFC. Мы нашли, что ми не требуется написать библиотеку классов, не требуется для написания приложений с серьезными. Чтобы использовать ми или не может быть личное решение, поэтому мы оставим это решение для вас.

Так что вы хотите использовать ми?

Если вы уже знаете, как использовать ми, понимать взвешивать и хотите использовать MFC, этот обновить скажет вам что вы должны сделать. Некоторые из ограничений являются общие ограничения C++, другие введенные в MFC архитектуру.

Ниже описаны некоторые из технических вопросов как ми влияют на использование общих MFC идиомы. В конце этой технической записки, полное приложение MFC с использованием ми включена, который можно извлечь и скомпилировать.

CRuntimeClass

Сохранение и механизмы создания динамических объектов MFC используют структуру данных CRuntimeClass для уникальной идентификации классов. MFC связывает одну структуру этого типа с каждого динамических и/или сериализуемого класса в приложении. Эти структуры, инициализируются во время запуска приложения с помощью специальных статический объект типа AFX_CLASSINIT. Вам нужно не беспокоиться с осуществлением этой информации, как это может измениться между ревизиями MFC.

Текущая реализация CRuntimeClass не поддерживает множественное наследование сведений о типе во время выполнения. Это не означает, Ми нельзя использовать в приложении MFC, но если вы это сделаете, вы будете иметь определенные обязанности, при работе с объектами, которые имеют более чем один базовый класс.

Функция-член CObject::IsKindOf будет неправильно определяет тип объекта, если он имеет несколько базовых классов. Таким образом нельзя использовать CObject в качестве виртуального базового класса, и все вызовы функций-членов CObject такие как Serialize и оператор new нужно будет иметь масштабы квалификаторов так что C++ можно устранить неоднозначность вызова соответствующей функции. Если вы нашли на необходимость использования ми в MFC, то вы должны быть уверены сделать класс, содержащий базовый класс CObject левая класс в списке базовых классов.

Консультации по вопросам использования и злоупотребления, Ми содержатся расширенные стили программирования C++ и идиомы , Джеймс о. Коплин (Addison Wesley, 1992).

CObject - корень всех классов

Как вы знаете, все значительные классы прямо или косвенно наследоваться от класса от CObject. CObject не имеет каких-либо членов данных, но имеет некоторые функциональные возможности по умолчанию. При использовании ми, будет общим для наследования от CObjectдва или более-производных классов, например, CFrameWnd и CObList:

класс CListWnd: государственные CFrameWnd, публичных CObList
{
 ...
};
CListWnd myListWnd

В этом случае включается CObject дважды, что приводит к две проблемы:

Рекомендуемые шаги

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

класс CListW&nd: государственные CFrameWnd, публичных CObList
{
общественности:
 nbsp;  void * оператора new (size_t nSize)
        {возвращение CFrameWnd::operator new(nSize);}
    Оператор void удаление (void * p)
        {CFrameWnd::operator delete(p)};

void дамп (CDumpContent и постоянного тока)
        {CFrameWnd::Dump(dc);
          CObList::Dump(dc); }
     ...
}

Виртуальное наследование от CObject?

Вы можете спросить, «если вы фактически наследовать от CObject, не все проблемы неопределенности уйти?».

Даже в эффективной объектной модели Microsoft виртуальное наследование не так эффективно, как не виртуальное наследование (так же как множественное наследование не так эффективно, как единичное наследование в определенных случаях). Поскольку нет никаких членов данных от CObject, виртуальное наследование не требуется для предотвращения нескольких копий данных член базового класса.

Реальный ответ не виртуальное наследование не решит проблемы неопределенности, описанным выше. Например: дамп виртуальной функции-члена по-прежнему неоднозначен (так как CFrameWnd и CObList осуществлять его по-разному).

Поэтому мы рекомендуем после описанные выше шаги для предоставления значения:

CObject::IsKindOf и во время ввода

Набрав механизм среды выполнения, поддерживаемых MFC в CObject использует макрос DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL и IMPLEMENT_SERIAL. Они дают возможность выполнять проверку типа во время выполнения, чтобы обеспечить безопасное приведение подъемы.

Эти макросы только поддерживают один базовый класс и будет работать на ограниченной основе умножить унаследованных классов. Базовый класс, указанный вами в IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL должно быть первым (или слева) базового класса. Например,

класс CListWnd: государственные CFrameWnd, публичных CObList
{
 nbsp;  DECLARE_DY&NAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)

Это позволит вам ввести проверку только левая базового класса. Ничего система типов во время выполнения будет не знают о дополнительных оснований (CObList в данном случае).

CWnd и схемы сообщений

Для того чтобы системы карты сообщений MFC для правильной работы существует два дополнительных требования:

В приведенном выше примере CFrameWnd — первая база класс.

Некоторые примеры, которые не будут работать:

класс CTwoWi&ndows: государственные CFrameWnd, публичных CEdit
 nbsp;  { ... };
        / / Ошибка: две копии CWnd

класс CListEdit: государственные CObList, публичных CEdit
    { ... };
        / / Ошибка: CEdit (производных от CWnd) должна быть первым

Пример программы, с помощью ми

В следующем примере это автономное приложение, состоящее из одного класса, производного от CFrameWnd и CWinApp. Этот способ структурирования приложения не рекомендуется, но это пример маленького приложения MFC с одного класса.

Можно вырезать следующие программы и скопировать его на верхней части HELLOAPP.НПК в сингл наследование образцов MFC Генеральной HELLOAPP. Затем построить программу, как обычно.

#include <afxwin.h>

class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{ 
public:
    CHelloAppAndFrame()
        { }

    // Necessary evil for MI disambiguity
    void* operator new(size_t nSize)
        { return CFrameWnd::operator new(nSize); }
    void operator delete(void* p)
        { CFrameWnd::operator delete(p); }

// Implementation
    // CWinApp overrides
    virtual BOOL InitInstance();
    // CFrameWnd overrides
    virtual void PostNcDestroy();
    afx_msg void OnPaint();

    DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(CHelloAppAndFrame, CFrameWnd)
    ON_WM_PAINT()
END_MESSAGE_MAP()

// since the frame window is not allocated on the heap, we must
// override PostNCDestroy not to delete the frame object
void CHelloAppAndFrame::PostNcDestroy()
{
    // do nothing (do not call base class)
}

void CHelloAppAndFrame::OnPaint()
{
    CPaintDC dc(this);
    CRect rect;
    GetClientRect(rect);

    CString s = "Hello, Windows!";
    dc.SetTextAlign(TA_BASELINE | TA_CENTER);
    dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
    dc.SetBkMode(TRANSPARENT);
    dc.TextOut(rect.right / 2, rect.bottom / 2, s);
}

// Application initialization
BOOL CHelloAppAndFrame::InitInstance()
{
    // first create the main frame
    if (!CFrameWnd::Create(NULL, "Multiple Inheritance Sample",
        WS_OVERLAPPEDWINDOW, rectDefault))
        return FALSE;

    // the application object is also a frame window
    m_pMainWnd = this;          
    ShowWindow(m_nCmdShow);
    return TRUE;
}

CHelloAppAndFrame theHelloAppAndFrame;

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

Index