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

Эта заметка описывает использование множественного наследования (мм) с классами Microsoft foundation.Использование MI необходима с MFC.MI не используется во всех классах MFC и требуются записывает библиотека классов.

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

В конце этой технической заметки можно найти полное приложение MFC, использующего MI.

CRuntimeClass

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

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

Метод CObject::IsKindOf неверно определит тип объекта, если он имеет несколько базовых классов.Поэтому нельзя использовать CObject как виртуальный базовый класс, и все вызовы функции-членам CObject как CObject::Serialize и Новое CObject::operator должны содержать квалификаторы области, чтобы C++ мог неоднозначности соответствующий вызов функции.Когда программа использует MI в составе MFC, классу, который содержит базовый класс CObject необходимо левейшим классом в списке базовых классов.

Можно использовать оператор dynamic_cast.Приведение объекта с MI одному из его базовых классов требует компилятора использовать функции в предоставляемом базовом классе.Дополнительные сведения см. в разделе оператор dynamic_cast.

CObject - корневой элемент всех классов

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

class CListWnd : public CFrameWnd, public CObList
{
 ...
};
CListWnd myListWnd;

В этом случае CObject включенные 2 времени.Это означает, что требуется возможность неоднозначности любая ссылка на методы и операторы CObject.operator new и удаление оператора 2 оператора, который необходимо disambiguated.Другой пример, следующий код вызовет ошибку во время компиляции:

myListWnd.Dump(afxDump);
    // compile time error, CFrameWnd::Dump or CObList::Dump ?

Методы Reimplementing CObject

При создании нового класса, который имеет два или более базовых классов, производных CObject, необходимо повторно реализовать методы CObject, что необходимо использовать другие лица.Операторы new и delete являются обязательными, и Дамп рекомендуется.Следующие примеры reimplements операторы new и delete и метод Dump:

class CListWnd : public CFrameWnd, public CObList
{
public:
    void* operator new(size_t nSize)
        { return CFrameWnd::operator new(nSize); }
    void operator delete(void* p)
        { CFrameWnd::operator delete(p); }

    void Dump(CDumpContent& dc)
        { CFrameWnd::Dump(dc);
          CObList::Dump(dc); }
     ...
};

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

Может оказаться, что виртуального наследования CObject позволяет проблемы неоднозначности функции, но это не так.Так как никакие данные элемента в CObject нет необходимости виртуальное наследование предотвратить несколько копий данных члена базового класса.В первом примере, который ранее был отображен, виртуальный метод Dump все еще является неоднозначным, поскольку он реализован по-другому в CFrameWnd и CObList.Лучший способ удаления неоднозначности следуйте рекомендациям представленными в предыдущем разделе.

CObject::IsKindOf и тип среды выполнения

При вводе механизм среды выполнения, поддерживаемый MFC в CObject используется макрос DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL и IMPLEMENT_SERIAL.Эти макросы могут выполняться проверка типов на этапе выполнения для обеспечения защиты downcasts.

Эти макросы поддерживают только один базовый класс и будут работать в виде limited, multiply наследуемые классы.Базовый класс указывается в IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL должно быть первым (или левейшим) базового класса.Это размещение позволяет выполнять проверку типа для самого левого базового класса.Система типов на этапе выполнения ничего не знает о дополнительных базовых классах.В следующем примере системы во время выполнения сделает проверки типа по отношению к CFrameWnd, но ничего не знают о CObList.

class CListWnd : public CFrameWnd, public CObList
{
    DECLARE_DYNAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)

CWnd и сопоставления сообщения

Для сопоставления сообщения MFC работал, 2 дополнительных требований.

  • Должен содержать только один CWnd производный от базового класса.

  • CWnd производный базовый класс должен быть первым (или левейшим) базового класса.

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

class CTwoWindows : public CFrameWnd, public CEdit
    { ... };
        // error : two copies of CWnd

class CListEdit : public CObList, public CEdit
    { ... };
        // error : CEdit (derived from CWnd) must be first

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

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

#include <afxwin.h>

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

    // Necessary because of 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()

// because 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;

См. также

Другие ресурсы

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

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