TN016: Verwenden von C++-Multiple Vererbung mit MFC

Diese Notiz beschreibt, wie mehrere Vererbung (MI) mit der Microsoft Foundation Classes.

Warum Mehrfachvererbung?

Gibt es eine Debatte in der C++- und Objekt-orientierte Gemeinschaften über den Wert der MI. Die Visual C++-Compiler und Entwicklungsumgebung unterstützt voll und ganz MI.

Die MFC-Klassenbibliothek wurde konzipiert, so dass Sie nicht brauchen, zu verstehen, MI MFC verwenden. MI ist nicht in der MFC-Klassen verwendet. Wir haben festgestellt, dass MI nicht benötigt wird, um eine Bibliothek-Klasse zu schreiben, noch es erforderlich ist, für ernsthafte Anwendungen schreiben. MI oder nicht verwenden kann eine persönliche Entscheidung, so dass wir Ihnen die Entscheidung überlassen.

So Want You to Use MI?

Wenn Sie bereits wissen, wie man MI verwenden und verstehen der Leistung Kompromisse MFC verwenden möchten, wird dieser Technote Ihnen sagen, was Sie tun müssen. Einige Beschränkungen sind allgemeinen C++ Einschränkungen, andere werden durch die MFC-Architektur.

Im folgenden werden einige der technischen Fragen wie MI betreffend die Verwendung von gemeinsamen MFC Idiome. Am Ende dieser technische Hinweis, dass eine vollständige MFC-Anwendung mit MI enthalten ist können Sie extrahieren und kompilieren.

CRuntimeClass

Die Persistenz und dynamisches Objekt Erstellung Mechanismen der MFC verwenden die CRuntimeClass -Datenstruktur zur eindeutigen Identifizierung von Klassen. MFC ordnet einer Struktur dieser Art jede dynamische und/oder serialisierbare Klasse in der Anwendung. Diese Strukturen sind bei Startzeit der Anwendung unter Verwendung eines speziellen statischen Objekts vom Typ AFX_CLASSINITinitialisiert. Sie müssen nicht selbst mit der Umsetzung dieser Informationen betreffen, weil es wahrscheinlich zwischen den Revisionen des MFC zu ändern ist.

Die aktuelle Implementierung von CRuntimeClass unterstützt keine Mehrfachvererbung Laufzeit-Typinformationen. Dies bedeutet nicht, Sie können MI in der MFC-Anwendung nicht verwenden, aber wenn Sie dies tun, haben Sie bestimmte Verantwortlichkeiten beim Arbeiten mit Objekten, die mehr als eine Basisklasse verfügen.

Die CObject::IsKindOf -Memberfunktion wird den Typ eines Objekts nicht korrekt bestimmt, wenn es mehrere Basisklassen verfügt. Daher können keine CObject als virtuelle Basisklasse, und alle Aufrufe von CObject -Memberfunktionen wie z. B. die Serialize-Methode und der Operator neue müssen Bereich Qualifikation haben, also, dass C++ den entsprechenden Funktionsaufruf unterscheiden kann. Wenn Sie die Notwendigkeit, MI innerhalb von MFC verwenden finden, sollte dann man sicher, dass die Klasse, die die CObject -Basisklasse Klasse ganz links in der Liste der Basisklassen enthält sein.

Beratung über die Verwendung und Missbrauch der MI finden Sie unter Erweiterte C++ Programmierung Formatvorlagen und Idiome von James O. Coplien (Addison-Wesley, 1992).

CObject - die Wurzel aller Klassen

Wie Sie wissen, ableiten alle wesentliche Klassen direkt oder indirekt von CObject-Klasse. CObject muss keine Daten, aber hat einige Standardfunktionen. Bei der Verwendung von MI wird es gemeinsame erben von zwei oder mehr CObject-abgeleitete Klassen, z. B. ein CFrameWnd und eine CObList:

klasse CListWnd: öffentliche CFrameWnd, öffentliche CObList
{
 ...
};
CListWnd myListWnd

In diesem Fall ist die CObject enthalten zweimal, das führt zu zwei Problemen:

Empfohlene Schritte

Beim Erstellen einer neuen Klasse mit zwei oder mehr CObject Basisklassen abgeleitet werden, implementieren Sie diese CObject -Mitglied, die Sie erwarten Menschen zu verwenden. Betreiber neue und Löschen Sie obligatorisch sind, wird als Dump empfohlen. Zum Beispiel:

klasse CListWnd: öffentliche CFrameWnd, öffentliche CObList
{
Öffentliche:
 Nbsp;  Void * Operator neue (Size_t nSize)
        {return Sie CFrameWnd::operator new(nSize);}
    void-Operator löschen (leere p)
        {CFrameWnd::operator delete(p);}

privatevoid Dump (CDumpContent & dc)
        {CFrameWnd::Dump(dc);
          CObList::Dump(dc); }
     ...
}

Virtuelle Vererbung von CObject?

Sie Fragen sich vielleicht, "Wenn Sie CObject praktisch erben, nicht alle der Mehrdeutigkeit Probleme verschwinden?".

Auch in die effiziente Microsoft-Objektmodell ist virtueller Vererbung nicht so effizient wie nicht virtuelle Vererbung (genauso wie Mehrfachvererbung nicht so effizient wie einfache Vererbung in bestimmten Fällen). Da es keine Memberdaten in CObject gibt, ist virtueller Vererbung nicht erforderlich, um zu verhindern, dass mehrere Kopien von einer Basisklasse Memberdaten.

Die wirkliche Antwort ist Nein, virtueller Vererbung wird oben dargestellten Mehrdeutigkeit Probleme nicht lösen. Zum Beispiel: Dump virtuellen Member-Funktion ist immer noch mehrdeutig, (da CFrameWnd und CObList es unterschiedlich implementieren).

Wir empfehlen daher nach den oben genannten Schritten zu Begriffsklärung:

CObject::IsKindOf und Laufzeit eingeben

Der Common Language Runtime Eingabe Mechanismus von MFC in CObject unterstützt wird die Makros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL und IMPLEMENT_SERIALverwendet. Diese bieten die Möglichkeit, eine Überprüfung der Run-Time-Typ ermöglicht sichere Umwandlung-Downs tun.

Diese Makros nur eine einzige Basisklasse zu unterstützen und in begrenztem Umfang für mehrfach vererbte Klassen arbeiten. Die Basisklasse, die Sie in IMPLEMENT_DYNAMIC oder IMPLEMENT_SERIAL angeben sollte die erste (oder linke)-Basisklasse. Zum Beispiel,

klasse CListWnd: öffentliche CFrameWnd, öffentliche CObList
{
 &Nbsp;  DECLARE_DYNAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)

Dies ermöglicht Ihnen, Überprüfung der am weitesten links Basisklasse nur eingeben. Das Run-Time-Typ-System wissen nichts über zusätzliche Basen (CObList in diesem Fall).

CWnd und Meldungszuordnungen

In Reihenfolge für das MFC-Nachrichtensystem-Karte ordnungsgemäß funktioniert gibt es zwei zusätzliche Anforderungen:

Im obigen Beispiel ist CFrameWnd die erste Base-Klasse.

Einige Beispiele, die nicht funktionieren:

klasse CTwoWindows: öffentliche CFrameWnd, öffentliche CEdit
 &Nbsp;  { ... };
        / / Fehler: zwei Kopien von CWnd

Klasse CListEdit: öffentliche CObList, öffentliche CEdit
    { ... };
        / / Fehler: CEdit (abgeleitet von CWnd) muss die ersten sein

Ein Beispielprogramm mit MI

Im folgende Beispiel ist eine eigenständige Anwendung, die eine von CFrameWnd und CWinAppabgeleiteten Klasse besteht. Diese Art der Strukturierung einer Anwendungs ist keine empfohlene, aber dies ist ein Beispiel für die kleinsten MFC-Anwendung mit einer Klasse.

Sie können das folgende Programm Ausschneiden und kopieren es über HELLOAPP.CPP im allgemeinen MFC-Beispiel einfach vererbte HELLOAPP. Erstellen Sie anschließend das Programm wie gewohnt.

#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;

Technische Hinweise von &Nummer |nbsp; Technische Hinweise nach Kategorie

Index