TN016: Použití vícenásobné dědičnosti jazyka C++ v prostředí MFC
Tato poznámka popisuje použití vícenásobné dědičnosti (MI) s Microsoft Foundation Classes.Použití MI není vyžadován s knihovnou MFC.MI se nepoužívá ve všech tříd knihovny MFC a není povinen napsat knihovnu tříd.
Následující dílčí témata popisují, jak MI ovlivňuje využívání společné MFC idioms stejně jako které se vztahuje některá omezení MI.Některé z těchto omezení jsou omezení obecného jazyka C++.Ostatní jsou uložené MFC architekturu.
Na konci této technické poznámce najdete kompletní aplikaci knihovny MFC, která používá MI.
CRuntimeClass
Perzistence a mechanismy vytváření dynamických objektů knihovny MFC použití CRuntimeClass datovou strukturu pro jednoznačnou identifikaci třídy.MFC jeden z těchto struktur přidruží každý dynamický nebo serializovatelné třídy ve vaší aplikaci.Tyto struktury jsou inicializovány při spuštění aplikace pomocí speciální statický objekt typu AFX_CLASSINIT.
Aktuální provádění CRuntimeClass MI informace o typu runtime nepodporuje.To však neznamená, že MI nelze použít v aplikaci knihovny MFC.Při práci s objekty, které mají více než jeden základní třídy, však bude mít určité povinnosti.
CObject::IsKindOf Metoda nesmí nesprávně určí typ objektu, pokud má více základních tříd.Proto nelze použít třídy CObject jako virtuální základní třídy a všechna volání CObject členské funkce, jako CObject::Serialize a Nový CObject::operator musí mít Kvalifikátory oboru tak, že C++ lze rozlišit volání příslušnou funkci.Pokud program používá MI v rámci knihovny MFC, třídy, která obsahuje CObject základní třída musí být třída nejvíce vlevo v seznamu základních tříd.
Další možností je použít dynamic_cast operátor.Obsazení objektu se MI na jeden z jeho základních tříd způsobí, že kompilátor v základní třídy zadané funkce.Další informace naleznete v tématu dynamic_cast – operátor.
Třídy CObject - kořen všech tříd
Všechny významné třídy jsou odvozeny přímo nebo nepřímo z třídy CObject.CObjectneobsahuje žádná data člen, ale má některé výchozí funkce.Při použití MI je obvykle zdědí od dvou nebo více CObject-odvozené třídy.Následující příklad ukazuje, jak lze zdědit třídu CFrameWnd a CObList:
class CListWnd : public CFrameWnd, public CObList
{
...
};
CListWnd myListWnd;
V tomto případě CObject je součástí dvakrát.To znamená, že potřebujete způsob, jak odstranit všechny odkazy na CObject metodami nebo operátorů.operator new a operátor delete jsou dva operátory, které musí být jednoznačně rozlišit.Například následující kód způsobí chybu v čase kompilace:
myListWnd.Dump(afxDump);
// compile time error, CFrameWnd::Dump or CObList::Dump ?
Metody reimplementing třídy CObject
Při vytvoření nové třídy, který má dva nebo více CObject základní třídy odvozené měli přeimplementovat CObject metod, které chcete ostatním uživatelům.Operátory new a delete jsou povinné a Výpis je doporučeno.Následující příklad reimplements new a delete subjekty a Dump metoda:
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); }
...
};
Virtuální dědičnost třídy CObject
To může zdát, že prakticky dědění CObject by vyřešit problém funkce nejednoznačnosti, ale to není tento případ.Protože nejsou žádná data člen v CObject, není třeba virtuální dědičnost zabránit více kopií dat člen základní třídy.V prvním příkladu, uvedený výše Dump virtuální metoda je stále nejednoznačný, protože je implementována odlišně v CFrameWnd a CObList.Nejlepší způsob, jak odebrat nejednoznačnosti je dodržovat doporučení uvedené v předchozí části.
CObject::IsKindOf a zadáním příkazu Run-Time
The run-time typing mechanism supported by MFC in CObject uses the macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL and IMPLEMENT_SERIAL.Tato makra můžete provést k zajištění bezpečné downcasts kontrola typu v době spuštění.
Tato makra podporují pouze jeden základní třídy a bude fungovat v omezené míře násobit zděděných tříd.Základní třídy v IMPLEMENT_DYNAMIC nebo IMPLEMENT_SERIAL by měla být základní třídy první (nebo nejvíce vlevo).Toto umístění vám umožní kontrolu základní třídy nejvíce vlevo pouze typu.Typu v době spuštění systému bude vědět nic o další základní třídy.V následujícím příkladu běhu systémů provede kontrolu proti typu CFrameWnd, ale nic vědět o CObList.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd mapování a zprávy
Systém mapu zpráv MFC pracovat správně existují dva další požadavky:
Musí být pouze jeden CWnd-základní třídy odvozené.
CWnd-Odvozené třídy základní musí být základní třída první (nebo nejvíce vlevo).
Zde je několik příkladů, které nefungují:
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
Ukázka programu pomocí MI
Následující ukázka je samostatná aplikace, která se skládá z jedné třídy odvozené z CFrameWnd a CWinApp.Nedoporučujeme struktury aplikace tímto způsobem, že toto je příklad nejmenší aplikace knihovny MFC, která obsahuje jednu třídu.
#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;