Condividi tramite


TN016: Mediante ereditarietà multipla C++ con MFC

Questa nota viene descritto come utilizzare l'ereditarietà multipla (MI) con le classi MFC (Microsoft Foundation Class).L'utilizzo di MI non è obbligatorio con MFC.Il MI non è utilizzato in alcune classi MFC e non è necessario scrivere una libreria di classi.

I seguenti sottotemi descritto come il MI riguarda l'utilizzo di idiomi comuni MFC nonché analizzano alcune delle limitazioni di MI.Alcune di queste limitazioni sono le restrizioni generali di C++.Altri vengono imposti da MFC.

Alla fine di questa nota tecnica di un'applicazione completa MFC che utilizza il MI.

CRuntimeClass

La persistenza e i meccanismi dinamici di creazione di un oggetto di MFC utilizzano la struttura dei dati di CRuntimeClass per identificare in modo univoco le classi.MFC associa una di queste strutture con ogni classe dinamica e/o serializzabile nell'applicazione.Queste strutture vengono inizializzate all'avvio mediante un oggetto statico speciale di tipo AFX_CLASSINIT.

L'implementazione corrente di CRuntimeClass non supporta le informazioni sul tipo di runtime di MI.Ciò non significa che non è possibile utilizzare il MI nell'applicazione MFC.Tuttavia, sarà determinate responsabilità quando si utilizzano oggetti che dispongono di più di uno classe base.

Il metodo di CObject::IsKindOf correttamente non determinerà il tipo di oggetto se dispone di più classi base.Di conseguenza, non è possibile utilizzare CObject come classe base virtuale e tutte le chiamate alle funzioni membro di CObject come CObject::Serialize e a CObject::operator nuovo devono avere qualificatori di ambito in modo che sia possibile rendere semplice C++ la chiamata di funzione appropriata.Quando un programma utilizza il MI all'interno di MFC, la classe che contiene la classe base di CObject deve essere una classe all'estrema sinistra nell'elenco di classi base.

In alternativa è possibile utilizzare l'operatore di dynamic_cast .Eseguendo il cast di un oggetto con il MI a una delle relative classi base impone il compilatore per utilizzare le funzioni della classe base fornita.Per ulteriori informazioni, vedere operatore di dynamic_cast.

CObject alla radice di tutte le classi

Tutte le classi significative derivano direttamente o indirettamente da una CObject.CObject non dispone di dati dei membri, ma alcune funzionalità predefinita.Quando si utilizza il MI, in genere erediteranno due o più da CObjectdi classi derivate.Nell'esempio seguente viene illustrato come la classe può ereditare da CFrameWnd e da CObList:

class CListWnd : public CFrameWnd, public CObList
{
 ...
};
CListWnd myListWnd;

In questo caso CObject è molto importate.Ciò significa che è necessario un metodo per rendere chiaro qualsiasi riferimento ai metodi o agli operatori di CObject .operator new e operator delete disponibili due operatori che devono essere resi semplici.Ad esempio, il codice seguente genera un errore in fase di compilazione:

myListWnd.Dump(afxDump);
    // compile time error, CFrameWnd::Dump or CObList::Dump ?

metodi di Reimplementing CObject

Quando si crea una nuova classe che dispone di due o più classi di base derivate CObject , reimplementare i metodi di CObject da altri utenti da utilizzare.Gli operatori new e delete sono obbligatori e dump è consigliato.I seguenti reimplements di esempio gli operatori di delete e di new e il metodo di Dump :

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); }
     ...
};

L'ereditarietà virtuale di CObject

Potrebbe sembrare che di ereditare CObject risolvesse il problema di ambiguità di funzione, ma non è questo il caso.Poiché non sono disponibili dati dei membri in CObject, non è necessario utilizzare l'ereditarietà virtuale di impedire più copie dei dati dei membri della classe base.Nel primo esempio che è indicato in precedenza, il metodo virtuale di Dump è ancora ambiguo perché viene implementato in modo diverso in CFrameWnd e in CObList.Il modo migliore per rimuovere l'ambiguità consiste nel seguire i requisiti definiti nella sezione precedente.

CObject::IsKindOf e digitazione in fase di esecuzione

Il meccanismo di runtime di digitazione supportato da MFC in CObject utilizza le macro DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL e IMPLEMENT_SERIAL.Queste macro possono eseguire un controllo del tipo di runtime per garantire il downcast sicuri.

Queste macro supportano una sola classe di base e funzioneranno in maniera limitata per moltiplicano le classi derivate.La classe base specificato in IMPLEMENT_DYNAMIC o IMPLEMENT_SERIAL deve essere la prima (o) classe base più a sinistra.Questo percorso consentirà di eseguire il controllo dei tipi per la classe di base più a sinistra.Il sistema di tipi di runtime non conoscerà alcuna operazione sulle classi di base aggiuntive.Nell'esempio seguente, i sistemi di runtime ulteriormente il controllo di tipo e CFrameWnd, ma non conosceranno alcuna operazione su CObList.

class CListWnd : public CFrameWnd, public CObList
{
    DECLARE_DYNAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)

CWnd e di mappe messaggi

Affinché il sistema della mappa messaggi MFC funzionino correttamente, esistono due requisiti aggiuntivi:

  • Deve essere presente un solo CWndla classe di base derivata.

  • CWnd- la classe base derivata sia la prima (o) classe base più a sinistra.

Di seguito sono riportati alcuni esempi che non funzionano:

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

Un esempio di programma mediante MI

L'esempio seguente è un'applicazione autonoma che è costituito da una classe derivata da CFrameWnd e da CWinApp.Non è consigliabile strutturate un'applicazione in questo modo, ma questo è un esempio di più piccola applicazione MFC che disponga di una classe.

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

Vedere anche

Altre risorse

Note tecniche del numero

Note tecniche per categoria