TN016: używanie dziedziczenia wielokrotnego języka C++ z MFC
Uwaga ta opisuje sposób używania wielokrotne dziedziczenie (MI) z Microsoft Foundation Classes.Użycie MI nie jest wymagane MFC.MI nie jest używane w żadnych klas MFC i nie jest wymagany na napisanie Biblioteka klas.
Następujące tematy podrzędne opisują, jak MI wpływa na wykorzystanie wspólnych MFC idiomów jak również obejmujące niektóre ograniczenia MI.Niektóre z tych ograniczeń są ogólne ograniczenie C++.Inne są nakładane przez architekturę MFC.
Na końcu tego Uwaga techniczna znajdziesz pełną aplikacji MFC, która używa MI.
CRuntimeClass
Trwałość i mechanizmy tworzenia obiektu dynamicznego stosowania MFC CRuntimeClass struktury danych do unikatowej identyfikacji klas.MFC kojarzy jednego z tych struktur z każdej klasy dynamicznej i/lub serializable w aplikacji.Te struktury są inicjowane podczas uruchamiania aplikacji za pomocą specjalnych statycznego obiektu typu AFX_CLASSINIT.
Obecna implementacja CRuntimeClass nie obsługuje MI informacje o typie runtime.To znaczy, że MI nie można używać w aplikacji MFC.Użytkownik będzie jednak niektóre obowiązki podczas pracy z obiektami, które mają więcej niż jednej klasy podstawowej.
CObject::IsKindOf Metoda będzie nie ustala poprawnie typ obiektu jeśli ma wiele klas podstawowych.W związku z tym, nie można użyć CObject jako wirtualne klasy podstawowej, a wszystkie wywołania CObject Członkowskich funkcji, takich jak CObject::Serialize i Nowy CObject::operator musi mieć zakres kwalifikatory, tak że C++ można odróżnić wywołanie odpowiedniej funkcji.Gdy program użyje MI w ramach MFC, klasa, która zawiera CObject klasy podstawowej musi być klasa lewej na liście klas bazowych.
Alternatywą jest użycie dynamic_cast operatora.Odlewnictwo obiektu z MI do jednej z jej klas podstawowych wymusi kompilator używania funkcji w klasie podstawowej dostarczone.Aby uzyskać dodatkowe informacje, zobacz Operator dynamic_cast.
CObject - katalog główny wszystkich klas
Wszystkie klasy znaczące pochodzić bezpośrednio lub pośrednio z klasy CObject.CObjectczy nie ma żadnych danych elementu członkowskiego, ale posiada ona niektóre funkcje domyślne.Kiedy używasz MI, będzie zazwyczaj dziedziczą z dwóch lub więcej CObject-klasy pochodne.Poniższy przykład ilustruje, jak dziedziczenie z klasy CFrameWnd i CObList:
class CListWnd : public CFrameWnd, public CObList
{
...
};
CListWnd myListWnd;
W tym przypadku CObject jest uwzględnione dwa razy.Oznacza to, że potrzebny jest sposób, aby odróżnić wszelkie odniesienia do CObject metod lub podmiotów gospodarczych.operator new i usunąć operatora są dwa operatory, które musi być sobie.Inny przykład poniższy kod powoduje błąd w czasie kompilacji:
myListWnd.Dump(afxDump);
// compile time error, CFrameWnd::Dump or CObList::Dump ?
Metody nabrały CObject
Podczas tworzenia nowej klasy ma dwa lub więcej CObject podstawowej klasy pochodne powinny reimplement CObject metod, które mają inni użytkownicy mają korzystać.Operatorzy new i delete są obowiązkowe i Dump jest zalecane.Poniższy przykład reimplements new i delete operatorów i 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); }
...
};
Wirtualne dziedziczenia CObject
Mogłoby się wydawać, że praktycznie dziedziczenie CObject rozwiąże problem niejednoznaczności funkcji, ale tak nie jest.Ponieważ nie ma żadnych danych elementu członkowskiego w CObject, nie ma potrzeby wirtualnego dziedziczenia zapobiegające wielu kopii danych członek klasy podstawowej.W pierwszym przykładzie, która okazała się wcześniej Dump metoda wirtualna jest nadal niejednoznaczna, ponieważ jest zaimplementowany inaczej w CFrameWnd i CObList.Najlepszym sposobem usunięcia niejednoznaczności jest stosuje się do zaleceń przedstawionych w poprzedniej sekcji.
CObject::IsKindOf i wpisując w czasie wykonywania
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.Te makra można wykonywać kontrola typów w czasie wykonywania do zagwarantowania bezpiecznego downcasts.
Te makra obsługuje tylko pojedynczy klasy podstawowej i będzie działać w sposób ograniczony dla klasy mnożenie dziedziczone.Klasy podstawowej określonej w IMPLEMENT_DYNAMIC lub IMPLEMENT_SERIAL powinien być pierwszym (lub lewej) klasy podstawowej.To rozmieszczenie umożliwi wpisanie sprawdzanie lewej klasy podstawowej tylko.System typów w czasie wykonywania zostanie nic nie wiedzą o dodatkowe klasy podstawowej.W poniższym przykładzie zrobi systemów wykonywania wpisz sprawdzanie przeciwko CFrameWnd, ale będzie nic nie wiedzą o CObList.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd i mapy wiadomości
Działał poprawnie, w systemie mapę wiadomość MFC są dwa dodatkowe wymagania:
Musi istnieć tylko jeden CWnd-klasy podstawowej.
CWnd-Pochodne klasy podstawowej musi być pierwszym (lub lewej) klasy podstawowej.
Oto kilka przykładów, które nie będzie działać:
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
Przykładowy Program przy użyciu MI
Poniższy przykład jest samodzielna aplikacja, która składa się z jednej klasy pochodzące z CFrameWnd i CWinApp.Firma Microsoft nie zaleca struktury aplikacji w taki sposób, że jest to przykład najmniejszą aplikacji MFC, która ma jedną klasę.
#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;