TN016: Verwenden von C++-Mehrfachvererbung mit MFC
In diesem Hinweis wird beschrieben, wie Sie mehrere Vererbungen (MI) mit den Microsoft Foundation-Klassen verwenden. Die Verwendung von MI ist bei MFC nicht erforderlich. MI wird nicht in MFC-Klassen verwendet und ist nicht erforderlich, um eine Klassenbibliothek zu schreiben.
Die folgenden Unterthemen beschreiben, wie MI sich auf die Verwendung gängiger MFC-Idiome auswirkt und einige der Einschränkungen von MI abdeckt. Einige dieser Einschränkungen sind allgemeine C++-Einschränkungen. Andere werden von der MFC-Architektur auferlegt.
Am Ende dieser technischen Notiz finden Sie eine vollständige MFC-Anwendung, die MI verwendet.
Cruntimeclass
Die Persistenz- und dynamischen Objekterstellungsmechanismen von MFC verwenden die CRuntimeClass-Datenstruktur , um Klassen eindeutig zu identifizieren. MFC ordnet eine dieser Strukturen jeder dynamischen und/oder serialisierbaren Klasse in Ihrer Anwendung zu. Diese Strukturen werden initialisiert, wenn die Anwendung mit einem speziellen statischen Objekt vom Typ AFX_CLASSINIT
beginnt.
Die aktuelle Implementierung unterstützt CRuntimeClass
keine MI-Laufzeittypinformationen. Dies bedeutet nicht, dass Sie MI nicht in Ihrer MFC-Anwendung verwenden können. Sie haben jedoch bestimmte Zuständigkeiten, wenn Sie mit Objekten arbeiten, die mehr als eine Basisklasse aufweisen.
Die CObject::IsKindOf-Methode bestimmt nicht ordnungsgemäß den Typ eines Objekts, wenn es über mehrere Basisklassen verfügt. Daher können Sie CObject nicht als virtuelle Basisklasse verwenden, und alle Aufrufe an CObject
Memberfunktionen wie CObject::Serialize und CObject::operator new müssen Bereichsqualifizierer aufweisen, damit C++ den entsprechenden Funktionsaufruf eindeutig machen kann. Wenn ein Programm MI in MFC verwendet, muss die Klasse, die die CObject
Basisklasse enthält, die linksste Klasse in der Liste der Basisklassen sein.
Eine Alternative besteht darin, den dynamic_cast
Operator zu verwenden. Das Umwandeln eines Objekts mit MI in eine seiner Basisklassen erzwingt die Verwendung der Funktionen in der bereitgestellten Basisklasse. Weitere Informationen finden Sie unter dynamic_cast Operator.
CObject – Der Stamm aller Klassen
Alle signifikanten Klassen werden direkt oder indirekt von der Klasse CObject
abgeleitet. CObject
verfügt nicht über Memberdaten, sie verfügt jedoch über einige Standardfunktionen. Wenn Sie MI verwenden, erben Sie in der Regel von zwei oder mehr CObject
abgeleiteten Klassen. Das folgende Beispiel veranschaulicht, wie eine Klasse von einem CFrameWnd und einer CObList erben kann:
class CListWnd : public CFrameWnd, public CObList
{
// ...
};
CListWnd myListWnd;
In diesem Fall CObject
ist zweimal enthalten. Dies bedeutet, dass Sie eine Möglichkeit benötigen, jegliche Verweise auf CObject
Methoden oder Operatoren zu disambiguieren. Der Operator "Neu " und "Operator löschen " sind zwei Operatoren, die mehrdeutig sein müssen. Als weiteres Beispiel verursacht der folgende Code zur Kompilierungszeit einen Fehler:
myListWnd.Dump(afxDump); // compile time error, CFrameWnd::Dump or CObList::Dump
Reimplementing CObject-Methoden
Wenn Sie eine neue Klasse mit zwei oder mehr CObject
abgeleiteten Basisklassen erstellen, sollten Sie die CObject
Methoden erneut anwenden, die andere Personen verwenden sollen. Operatoren new
und sind obligatorisch und delete
Dump wird empfohlen. Im folgenden Beispiel werden die new
Operatoren und delete
operatoren und die Dump
Methode neu gestaltet:
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);
}
// ...
};
Virtuelle Vererbung von CObject
Es mag so aussehen, dass die virtuelle Vererbung CObject
das Problem der Funktionsdeutigkeit lösen würde, aber das ist nicht der Fall. Da keine Memberdaten vorhanden CObject
sind, benötigen Sie keine virtuelle Vererbung, um mehrere Kopien einer Basisklassenmememmdaten zu verhindern. Im ersten Beispiel, das zuvor gezeigt wurde, ist die Dump
virtuelle Methode immer noch mehrdeutig, da sie anders CFrameWnd
in und CObList
implementiert wird. Die beste Möglichkeit, Mehrdeutigkeit zu entfernen, besteht darin, die im vorherigen Abschnitt dargestellten Empfehlungen zu befolgen.
CObject::IsKindOf und Laufzeiteingabe
Der von MFC CObject
unterstützte Laufzeiteingabemechanismus verwendet die Makros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL und IMPLEMENT_SERIAL. Diese Makros können eine Laufzeittypüberprüfung durchführen, um sichere Downcasts zu gewährleisten.
Diese Makros unterstützen nur eine einzelne Basisklasse und funktionieren auf begrenzte Weise für multiplizierte geerbte Klassen. Die Basisklasse, die Sie in IMPLEMENT_DYNAMIC oder IMPLEMENT_SERIAL angeben, sollte die erste (oder linksste) Basisklasse sein. Mit dieser Platzierung können Sie die Typüberprüfung nur für die linksste Basisklasse durchführen. Das Laufzeittypsystem kennt nichts über zusätzliche Basisklassen. Im folgenden Beispiel führen die Laufzeitsysteme die Typüberprüfung durch CFrameWnd
, wissen aber nichts über CObList
.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
// ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd- und Nachrichten-Karten
Damit das MFC-Nachrichtenzuordnungssystem ordnungsgemäß funktioniert, gibt es zwei zusätzliche Anforderungen:
Es darf nur eine
CWnd
abgeleitete Basisklasse vorhanden sein.Die
CWnd
abgeleitete Basisklasse muss die erste (oder linksste) Basisklasse sein.
Hier sind einige Beispiele, die nicht funktionieren:
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
Beispielprogramm mit MI
Das folgende Beispiel ist eine eigenständige Anwendung, die aus einer klasse besteht, die von CFrameWnd
und CWinApp abgeleitet ist. Es wird nicht empfohlen, eine Anwendung auf diese Weise zu strukturieren, aber dies ist ein Beispiel für die kleinste MFC-Anwendung mit einer Klasse.
#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;
Siehe auch
Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet