TN016: Použití dědičnosti více C++ s MFC
Tato poznámka popisuje použití dědičnosti více (MI) s Microsoft Foundation Classes.Použití MI není nutné MFC.MI nepoužívá žádné třídy MFC a nevyžaduje zápis knihovna tříd.
Následující témata popisují, jak MI ovlivňuje použití běžných MFC idioms stejně jako krytí některých omezení MI.Tato omezení některých obecných omezení C++.Ostatní jsou uložené architektury MFC.
Na konci této technické poznámky najdete kompletní aplikaci MFC, který používá MI.
CRuntimeClass
Perzistence a mechanismy vytváření dynamických objektů MFC použití CRuntimeClass strukturu dat k jednoznačné identifikaci tříd.MFC jeden z těchto struktur přidruží každý dynamické nebo serializovatelné třídy aplikace.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 informace o typu runtime MI nepodporuje.To neznamená, že MI nelze použít v aplikaci MFC.Budou však mít určité odpovědnosti při práci s objekty, které mají více než jeden základní třídy.
CObject::IsKindOf Metoda bude nesprávně určí typ objektu má několik základních tříd.Proto nelze použít CObject jako virtuální základní třídy a všechna volání CObject členské funkce, jako například CObject::Serialize a Nový CObject::operator musí mít Kvalifikátory oboru tak, že C++ může disambiguate volání příslušné funkce.Pokud program používá MI v rámci MFC, třída obsahující CObject základní třídy musí být třída nejvíce vlevo v seznamu základní třídy.
Alternativou je použití dynamic_cast operátor.Obsazení objektu se MI jednoho z jeho základních tříd vynutíte základní třídy zadané pomocí funkcí kompilátoru.Další informace naleznete v tématu dynamic_cast operátora.
CObject - kořen všech tříd
Všechny významné třídy odvozena od třídy přímo nebo nepřímo CObject.CObjectnemá nutné data všech členů, ale neobsahuje proto některé funkce výchozí.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 může zdědit třídy CFrameWnd a CObList:
class CListWnd : public CFrameWnd, public CObList
{
...
};
CListWnd myListWnd;
V tomto případě CObject je součástí dvakrát.To znamená, že je třeba, aby jakýkoli odkaz na disambiguate CObject metod nebo operátorů.operator new a operátor delete jsou dva operátory, které musí být jednoznačně rozlišit.Následující kód například způsobí chybu v době kompilace:
myListWnd.Dump(afxDump);
// compile time error, CFrameWnd::Dump or CObList::Dump ?
Reimplementing CObject metody
Při vytvoření nové třídy má dvě nebo více CObject základní třídy odvozené by reimplement 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 operátory 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ědění CObject
Může zdát, že prakticky dědění CObject by vyřešen nejednoznačnosti funkce však není případ.Protože neexistuje žádný člen dat v CObject, není nutné virtuální dědění zabránit více kopií dat členů základní třídy.V prvním příkladu uvedený dříve Dump virtuální metoda je stále dvojznačný, protože je implementováno jiným způsobem v CFrameWnd a CObList.Je nejlepší způsob, jak odstranit dvojznačnosti postupujte podle 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 kontrolu typu run-time pro zaručení bezpečné downcasts.
Tato makra podporují pouze jeden základní třídy a bude pracovat v omezené míře pro násobit zděděné třídy.Základní třídy v IMPLEMENT_DYNAMIC nebo IMPLEMENT_SERIAL by měla být základní třída první (nebo nejvíce vlevo).Toto umístění umožňují základní třídy nejvíce vlevo jen kontrolu typu.Typ spuštění systému vědět nic o další základní třídy.V následujícím příkladu bude systémů spuštění provést 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
MFC zpráva mapa systému pracovat správně jsou dva dodatečné požadavky:
Musí existovat pouze jedna CWnd-základní třída odvozena.
CWnd-Základní třída odvozená musí být základní třída první (nebo nejvíce vlevo).
Zde jsou některé příklady, které nebude fungovat:
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ázkový Program pomocí MI
Následující příklad je samostatná aplikace, která se skládá z jedné třídy odvozené z CFrameWnd a CWinApp.Nedoporučujeme struktury aplikace tímto způsobem, ale toto je příklad nejmenší MFC aplikace, která má 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;