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;