Questa nota viene descritto come utilizzare più ereditarietà (MI) con il Microsoft Foundation Classes.
Perché l'ereditarietà multipla?
Il valore di MI c'è un dibattito in corso in C++ e comunità orientate agli oggetti. L'ambiente del compilatore e di sviluppo di Visual C++ supporta pienamente MI.
La libreria di classi MFC è stata progettata in modo che non dovete capire MI utilizzare MFC. Non MI viene utilizzato in una qualsiasi delle classi MFC. Abbiamo trovato che MI non è necessario scrivere una libreria di classi, né essa è necessaria per la scrittura di applicazioni gravi. Utilizzare MI o non può essere una decisione personale, quindi noi lasciare tale decisione a voi.
Così si vuole utilizzare MI?
Se già capire come utilizzare MI, capire le prestazioni trade-off e desidera utilizzare MFC, questa nota tecnica vi dirà quello che devi fare. Alcune delle restrizioni sono restrizioni generali di C++, gli altri sono imposti dall'architettura MFC.
Di seguito sono descritte alcune delle questioni tecniche di come MI interessano l'uso di frasi idiomatiche MFC comuni. Alla fine di questa nota tecnica una completa applicazione MFC usando MI è incluso che puoi estrarre e compilare.
CRuntimeClass
La persistenza e meccanismi di creazione oggetto dinamico di MFC utilizzano la struttura di dati CRuntimeClass per identificare in modo univoco le classi. Ogni classe serializzabile e/o dinamico nell'applicazione MFC associa una struttura di questo tipo. Queste strutture vengono inizializzate in fase di avvio applicazione utilizzando un oggetto speciale statico del tipo AFX_CLASSINIT. Bisogno di non riguardano te stesso con l'implementazione di queste informazioni, come è destinata a cambiare tra revisioni di MFC.
L'implementazione corrente di CRuntimeClass non supporta informazioni sul tipo di ereditarietà multipla runtime. Questo non significa non è possibile utilizzare MI nell'applicazione MFC, ma se lo fai, avrà certe responsabilità quando si lavora con oggetti che hanno più di una classe base.
La funzione membro CObject::IsKindOf non correttamente determina il tipo di un oggetto se ha più classi di base. Pertanto, non è possibile utilizzare CObject come classe base virtuale, e tutte le chiamate alle funzioni membro CObject come Serialize e nuovo operatore dovrà avere qualificatori di portata così quel C++ può risolvere l'ambiguità tra la chiamata di funzione appropriata. Se trovate la necessità di utilizzare MI all'interno di MFC, allora si dovrebbe essere sicuro di fare la classe contenente la classe base CObject la classe più a sinistra nell'elenco delle classi base.
Per consigli sugli usi e abusi di MI, vedere stili di programmazione C++ avanzata ed idiomi da James O. Coplien (Addison Wesley, 1992).
CObject - la radice di tutte le classi
Come sapete, tutte le classi significative derivano direttamente o indirettamente dalla classe CObject. CObject non hanno alcun membro dati, ma ha alcune funzionalità di default. Quando si utilizza MI, sarà comune a eredita da due o più CObject-classi, ad esempio, un CFrameWnd e un CObList derivate:
classe CListWnd: CFrameWnd pubblici, pubblica CObList
{
...
};
CListWnd myListWnd
In questo caso CObject è incluso due volte, il che porta a due problemi:
myListW&nd.Dump(afxDump);
nbsp; / / compile error tempo, CFrameWnd::Dump o CObList::Dump
Procedura consigliata
Quando la creazione di una nuova classe con due o più CObject base classi derivate, reimplementare quei membri CObject che ci si aspetta la gente usi. Nuovi operatori ed eliminare sono obbligatorie, Dump è raccomandato. Ad esempio:
classe CListWnd: CFrameWnd pubblici, pubblica CObList
{
pubblica:
nbsp; void * operatore new (size_t nSize)
{return CFrameWnd::operator new(nSize);}
Operatore void delete (vuoto p)
{CFrameWnd::operator delete(p);}
public static void Dump (CDumpContent & dc)
{CFrameWnd::Dump(dc);
CObList::Dump(dc); }
...
}
Ereditarietà virtuale di CObject?
Potrebbe chiedere, "se si eredita da CObject praticamente, non tutti i problemi di ambiguità andrà via?".
Anche nel modello a oggetti Microsoft efficiente ereditarietà virtuale non è efficiente quanto l'ereditarietà non virtuale (proprio come l'ereditarietà multipla non è efficiente quanto l'ereditarietà singola in taluni casi). Dal momento che non ci sono dati membro in CObject, ereditarietà virtuale non è necessaria per evitare che più copie dei dati dei membri della classe base.
La vera risposta è no, ereditarietà virtuale non risolverà i problemi di ambiguità illustrati sopra. Per esempio: la funzione membro virtual Dump è ancora ambigua (in quanto CFrameWnd e CObList implementarla in modo diverso).
Pertanto si consiglia di seguendo la procedura sopra indicata per fornire disambigua:
CObject::IsKindOf e digitando Runtime
Il meccanismo di battitura a macchina di runtime supportato da MFC in CObject utilizza le macro DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL e IMPLEMENT_SERIAL. Queste offrono la possibilità di fare un controllo di tipo runtime per consentire la cassaforte cast-Bassi.
Queste macro solo sostengono una sola classe base e funzioneranno in modo limitato per classi ereditate moltiplica. Classe base specificata in IMPLEMENT_DYNAMIC o IMPLEMENT_SERIAL dovrebbe essere la classe base prima (o più a sinistra). Ad esempio,
classe CListWnd: CFrameWnd pubblici, pubblica CObList
{
nbsp; DECLARE_DY&NAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)
Questo vi permetterà di controllo per la classe base più a sinistra solo dei tipi. Il sistema dei tipi in fase di esecuzione non saprà nulla su basi supplementari (CObList in questo caso).
CWnd e mappe messaggi
Affinché il sistema di mappa messaggi MFC funzionare correttamente, ci sono due requisiti supplementari:
Nell'esempio precedente, CFrameWnd è la classe prima base.
Alcuni esempi che non funzionerà:
classe CTwoWi&ndows: CFrameWnd pubblica, CEdit pubblica
nbsp; { ... };
/ / error: due copie di CWnd
classe CListEdit: CObList pubblica, CEdit pubblica
{ ... };
/ / error: CEdit (derivato da CWnd) deve essere il primo
Un programma di esempio utilizzando MI
L'esempio seguente è un'applicazione stand-alone che consiste in una classe derivata da CFrameWnd e CWinApp. Questo modo di strutturare un'applicazione non è un raccomandato, ma questo è un esempio dell'applicazione MFC più piccolo con una sola classe.
È possibile tagliare il seguente programma e copiarlo su HELLOAPP.CPP nel campione generale MFC ereditarietà singola HELLOAPP. Quindi compilare il programma normalmente.
#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;
&Note tecniche per numero |nbsp; Note tecniche per la categoria