Udostępnij za pośrednictwem


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;

Zobacz też

Inne zasoby

Uwagi techniczne według numerów

Uwagi techniczne według kategorii