Corso di Visual C++

Le classi base CObject, CWinApp e CDocument di MFC

 

Nella scorsa puntata abbiamo trattato il sistema di gestione dei messaggi, ora inizieremo ad esplorare la gerarchia delle classi della MFC e vedremo come cercare di sfruttarla in base alle nostre esigenze.

Per visualizzare la mappa gerarchica MFC, clicca qui, per scaricarla in formato .zip clicca qui: Immagine compressa Gerarchia Classi
Osservando la mappa gerarchica vediamo che la maggioranza delle classi deriva, direttamente o indirettamente, da CObject.

Il motivo è che tale classe fornisce quattro servizi molto utili:

Tramite la serializzazione è possibile, in un modo molto semplice che vedremo più avanti, salvare e caricare archivi da oggetti di memorizzazione come dischi o nastri. Le due funzioni principali sono IsSerializable e Serialize.

Per utilizzare tale supporto nelle proprie classi derivate, bisogna includere la macro DECLARE_SERIAL nella dichiarazione di classe e IMPLEMENT_SERIAL nell'implementazione.

Esempio:

// MyClass.hclass CMyClass: public CObject
{
public:
CMyClass();
void Serialize( CArchive& archive );

DECLARE_SERIAL(CMyClass)
};// MyClass.cpp
#include "stdafx.h"
#include "MyClass.h"

IMPLEMENT_SERIAL( CMyClass, CObject, VERSIONABLE_SCHEMA | 2 )

...

Il supporto per le informazioni a Run-Time ci permette di avere informazioni su una classe avendo un puntatore ad un oggetto di una classe derivata da CObject; tali informazioni consistono ad esempio nel nome della classe o nel grado di parentela con altre classi. Le funzioni principali sono IsKindOf e GetRuntimeClass.

Si vuole far notare che tale supporto è uno strumento completamente diverso dal meccanismo di identificazione a run-time del C++ (RTTI).

Per utilizzare tale supporto nelle proprie classi derivate, bisogna includere la macro DECLARE_DYNAMIC nella dichiarazione di classe e IMPLEMENT_DYNAMIC nell'implementazione (vedere MSDN per approfondimenti).

Infine il supporto di debugging e di diagnostica ci mette a disposizione le due funzioni AssertValid e Dump che rispettivamente verificano lo stato interno di un oggetto e che mandano in output, nella finestra di Debug, informazioni diagnostiche da noi specificate.

Inoltre CObject sovraccarica gli operatori di allocazione dinamica della memoria, new e delete, per tenere traccia degli oggetti allocati; questa precauzione serve ad avvisarci su eventuali sprechi di memoria alla chiusura dell'applicazione quando dimentichiamo di deallocare oggetti creati dinamicamente.

La compatibilità con le classi di collezione ci permette di utilizzare gli oggetti della nostra classe con oggetti collezione di tipo CObList o CObArray.

Microsoft consiglia di derivare tutte le proprie classi da CObject dato che oltre a tutti i vantaggi suddetti si ha un occupazione di spazio aggiuntiva veramente minima ( un oggetto di tipo CRuntimeClass e una VTable per quattro funzioni virtuali).

Altre classi base molto importanti per la nostra applicazione sono CWinApp, CDocument e CWnd.

CWinApp rappresenta l'applicazione nel vero senso del termine e comprende funzioni molto utili come ProcessShellCommand che elabora gli argomenti della linea di comando e OnFileOpen che implementa il comportamento del comando Apri del menu File. CWinApp è derivata da CWinThread che rappresenta il thread di esecuzione e che gestisce il comportamento del thread (priorità, Suspend, Resume...).

CDocument contiene funzioni membro per poter accedere ai file e alle viste ad esso collegate. Una funzione che useremo spesso di tale classe è SetModifiedFlag che ci segnala se il nostro documento è stato modificato e come effetto secondario ci fa apparire nella barra del titolo un asterisco accanto al nome del documento. La funzione IsModified ci ritorna non-zero se il documento è stato modificato, 0 altrimenti.

Altre tre funzioni spesso utilizzate sono OnNewDocument, OnOpenDocument e OnSaveDocument che vengono richiamate per creare, aprire e salvare un documento; si noti che queste funzioni sono dichiarate virtuali ed occorre scavalcarle nel caso si voglia cambiare il comportamento di default.

Nel caso si scavalchi una di queste funzioni, non si deve dimenticare di richiamare la versione della classe base a meno che non si sa perfettamente cosa si sta facendo e quali sono poi le conseguenze; per fare un esempio se si scavalca OnNewDocument e non si richiama la versione della classe base (CDocument::OnNewDocument) non verrà mai richiamata la funzione DeleteContents che ripulisce il documento prima dell'utilizzo, lo marca come non-modificato e si assicura che sia vuoto.

Sia CWinThread che CDocument derivano da CCmdTarget che permette un sofisticato sistema di smistamento dei messaggi e trasformano la classe in un command target o "obiettivo di comando" in grado di ricevere ed elaborare i messaggi. Senza la derivazione da tale classe i messaggi potrebbero essere elaborati solo nella classe derivata da CWnd in cui vengono ricevuti.

 

Torna all'indice Generale del corso di Corso di Visual C++ di Software Planet