Uwaga ta opisuje sposób używania wielu dziedziczenie (MI) w programie Microsoft Foundation Classes.
Dlaczego dziedziczenie wielokrotne?
Istnieje dyskusji w C++ i obiektowo Wspólnot nad wartością MI. Visual C++ kompilatora i rozwój środowiska naturalnego w pełni popiera MI.
Biblioteka klas MFC został zaprojektowany tak, aby nie trzeba zrozumieć MI używania MFC. MI nie jest używany w żadnym z klas MFC. Stwierdziliśmy, że MI nie jest wymagane aby zapisać bibliotekę klas, ani nie jest wymagane do pisania aplikacji poważne. Aby użyć MI lub nie może być osobista decyzja, więc mamy zostawić tę decyzję do Ciebie.
Dlatego warto wykorzystania MI?
Jeśli już zrozumienie sposobu używania MI, zrozumieć kompromis wydajność i chcesz używać MFC, to technote wskażą co należy zrobić. Niektóre ograniczenia są ogólne ograniczenia C++, inni są nałożone przez architekturę MFC.
Poniżej opisano niektóre z problemów technicznych związanych z jak MI wpływ na wykorzystanie wspólnych Idiomy MFC. Na końcu tego Uwaga techniczna kompletnych aplikacji MFC przy użyciu MI jest włączone w której można wyodrębnić i skompilować.
CRuntimeClass
Trwałość i mechanizmy tworzenia dynamicznych obiektów MFC struktury danych CRuntimeClass należy użyć do unikatowej identyfikacji klas. MFC kojarzy jednej struktury tego typu z każdej klasy dynamicznej i/lub można zaszeregować w aplikacji. Struktury te są inicjowane w chwili uruchomienia aplikacji przy użyciu specjalnego obiektu statycznego typu AFX_CLASSINIT. Użytkownik nie musi dotyczyć samodzielnie z wprowadzenia w życie tych informacji, jest prawdopodobne zmienić między poszczególnymi wersjami MFC.
Obecna implementacja CRuntimeClass nie obsługuje wielokrotne dziedziczenie runtime typu informacji. Nie oznacza to MI nie można używać w aplikacji MFC, ale, jeśli będą miały pewnych obowiązków, podczas pracy z obiektami, które mają więcej niż jednej klasy podstawowej.
Funkcję Państwa CObject::IsKindOf nie zostanie poprawnie określić typ obiektu, jeśli posiada wiele klas podstawowych. W związku z tym CObject nie można użyć jako wirtualnej klasy podstawowej, a wszystkie wywołania CObject Członkowskie funkcje takie jak Serialize i Nowy podmiot będzie musiała mieć zakres kwalifikatory, tak że C++ można disambiguate wywołanie odpowiedniej funkcji. Jeśli znajdziesz potrzebę używania MI w ramach MFC, następnie należy koniecznie Sprawdź, klasę zawierającego klasę podstawową CObject klasy lewego na liście klas podstawowych.
Porady na temat zastosowań i nadużycia MI zobacz Zaawansowane C++ Programming Style i Idiomy James O. Coplien (Addison Wesley, 1992).
CObject - główny wszystkich klas
Jak wiadomo, wszystkie klasy znaczące wynikają bezpośrednio lub pośrednio z klasy CObject. CObject nie ma żadnych danych, ale mają niektóre funkcje domyślne. Korzystając z MI, będzie on wspólne dla dziedziczą z dwóch lub więcej CObject-pochodnych klasy, na przykład CFrameWnd i CObList:
klasa CListWnd: CFrameWnd publicznych, publicznych CObList
{
...
};
CListWnd myListWnd
W tym przypadku CObject jest dołączone dwa razy, które prowadzi do dwóch problemów:
myListW&nd.Dump(afxDump);
nbsp; / Kompiluj błąd czasu wykonania, CFrameWnd::Dump lub CObList::Dump
Zalecanych kroków
Podczas tworzenia nowej klasy z dwóch lub więcej CObject pochodnych klas podstawowych, reimplement Członkowskim CObject z oczekiwaniami osób do używania. Nowe podmioty gospodarcze i usunąć są obowiązkowe, Dump jest zalecane. Na przykład:
klasa CListWnd: CFrameWnd publicznych, publicznych CObList
{
publiczne:
nbsp; void * operatora nowych (int nSize)
{return CFrameWnd::operator new(nSize);}
void operator delete (void * p)
{CFrameWnd::operator delete(p);}
Unieważnij zrzutu (CDumpContent & dc)
{CFrameWnd::Dump(dc);
CObList::Dump(dc); }
...
}
Wirtualny dziedziczenie CObject?
Zapytać, "Jeżeli praktycznie dziedziczą CObject nie wszystkie problemy niejednoznaczności odejdź?".
Nawet w skutecznego modelu obiektów programu Microsoft wirtualnego dziedziczenie nie jest jak najwydajniejszy-wirtualny dziedziczenie (podobnie jak dziedziczenie wielokrotne nie jest tak wydajna, jak pojedynczy dziedziczenia w niektórych przypadkach). Ponieważ nie istnieje żadne dane Państwa CObject, dziedziczenie wirtualny nie jest potrzebna zapobiegające wielu kopii danych klasy podstawowej.
Prawdziwą odpowiedzią jest brak, wirtualny dziedziczenie nie rozwiąże problemy niejednoznaczności, przedstawione powyżej. Na przykład: funkcja wirtualnego Członkowskie zrzutu jest nadal niejednoznaczne (od CFrameWnd i CObList wdrożyć ją inaczej).
Dlatego zaleca się po powyższe kroki zapewniające ujednoznacznienie:
CObject::IsKindOf i wpisując polecenie Run-time
Mechanizm wpisywania runtime, obsługiwanych przez usługi MFC w CObject używa makra DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL i IMPLEMENT_SERIAL. Te dają możliwość wyboru typów w czasie wykonywania umożliwiające bezpieczne oddanych downs.
Te makra tylko wsparcie jednej klasy bazowej i będzie działać w sposób ograniczony do mnożenie dziedziczone klasy. Klasy podstawowej, którą określono w IMPLEMENT_DYNAMIC lub IMPLEMENT_SERIAL powinny być klasą bazową pierwszego (lub lewego). Na przykład,
klasa CListWnd: CFrameWnd publicznych, publicznych CObList
{
nbsp; DECLARE_DY&NAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)
Umożliwi to wpisz sprawdzanie tylko klasy bazowej lewego. System typów w czasie wykonywania będzie wiadomo nic na temat podstaw dodatkowych (CObList w tym przypadku).
CWnd i mapy wiadomości
Aby system mapę wiadomości MFC, którego działa poprawnie istnieją dwie dodatkowe wymagania:
W powyższym przykładzie CFrameWnd jest klasa bazowa pierwszego.
Kilka przykładów, które nie będą działać:
klasa CTwoWi&ndows: CFrameWnd publicznych, publicznych CEdit
nbsp; { ... };
/ / Błąd: dwie kopie CWnd
Klasa CListEdit: CObList publicznych, publicznych CEdit
{ ... };
/ / Błąd: CEdit (pochodzące z CWnd) musi być pierwszym
Przykładowy Program przy użyciu MI
Poniższy przykład jest autonomiczną aplikację, która składa się z jednej klasy pochodzące z CFrameWnd i CWinApp. Ten sposób struktury aplikacji nie jest zalecane, ale jest to przykład najmniejszą Aplikacja MFC z jednej klasy.
Można wyciąć następujący program i skopiuj go nad HELLOAPP.CPP w próbce ogólnej MFC singel dziedziczenie HELLOAPP. Skonstruuj programu w zwykły.
#include <afxwin.h>
class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{
public:
CHelloAppAndFrame()
{ }
// Necessary evil for 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()
// since 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;
Uwagi techniczne przez liczbę |nbsp; Uwagi techniczne według kategorii