Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 23 di 43
  • livello principiante
Indice lezioni

Classi ed Oggetti: i metodi

Come funzionano e come vengono interpretati i metodi delle classi
Come funzionano e come vengono interpretati i metodi delle classi
Link copiato negli appunti

Metodi

Abbiamo visto in precedenza cosa è e come viene definito un metodo. Ora vediamo di
approfondire l'argomento. In Object Pascal ci sono due parole chiave che rientrano nella
creazione ed utilizzo dei metodi; queste sono Inherited e Self.

Inherited permette di richiamare i metodi dell'antenato da cui deriva
la classe a cui appartiene il nuovo metodo. Per meglio dire, se la classe da cui abbiamo
ereditato definisce un metodo MioMetodo scrivendo il codice seguente richiamo
quel metodo

Procedure MiaClasse.MioMetodo(Valore : Integer);

Begin

Inherited;

End;

Ciò permette di aggiungere funzionalità al metodo di base ereditato.

Non specificando nessun identificatore dopo la parola riservata Inherited,
questa farà riferimento al metodo dell'antenato avente lo stesso nome del metodo in cui
si trova la parola chiave Inherited. Se dopo di essa non viene
specificato alcun parametro, verranno passati al metodo ereditato gli stessi parametri
passati al metodo della classe che si sta definendo.

Se invece la parola Inherited è seguita da un identificatore di
metodo, questa effettuerà una chiamata al metodo corrispondente all'identificatore
indicato ricercandolo a partire dall'antenato immediato.

La parola chiave Self permette di fare riferimento, all'interno
dell'implementazione di un metodo, all'oggetto dal quale il metodo viene chiamato.

Collegamento dei metodi

I metodi possono essere di tre tipi: statici, virtuali, dinamici.
Quando un metodo viene definito, per impostazione predefinita è statico.

Nella chiamata di un metodo statico, l'implementazione da attivare viene determinata in
base al tipo, dichiarato in fase di compilazione, della classe che viene utilizzata per la
chiamata del metodo.

Quindi per esempio, avendo le dichiarazioni di classi seguenti

Type TClasse1 = Class

Procedure Metodo1;

End;

TClasse2 = Class(TClasse1)

Procedure Metodo1;

End;

ed eseguendo il seguente codice

Var Classe1 : TClasse1;

Classe2 : TClasse2;

Begin

Classe1 := TClasse1.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

Classe1.Destroy;

Classe1 := TClasse2.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

TClasse2(Classe1).Metodo1;

Classe1.Destroy;

Classe2 := TClasse2.Create;
//L'implementazione utilizzata è quelle di TClasse2

Classe2.Metodo1;

Classe2.Destroy;

End;

avremo che l'esecuzione della seconda chiamata "Classe1.Metodo1" determinerà
l'esecuzione dell'implementazione del metodo nella classe "TClasse1" anche se la
variabile "Classe1" fa riferimento ad un oggetto di tipo "TClasse2".

I metodi dichiarati virtual o dynamic possono essere ridefiniti nelle
classi discendenti. Per dichiarare dei metodi come virtual o dynamic
occorre specificare dopo la loro dichiarazione le parole chiave virtual o
dynamic. A differenza dei metodi static la determinazione
dell'implementazione del metodo da utilizzare nella chiamata viene determinata in base al
tipo run-time. Per ridefinire un metodo virtual o dynamic occorre
specificare, nella classe discendente, un metodo con lo stesso nome del metodo della
classe antenato e utilizzare la parola chiave override dopo la sua
dichiarazione.

Apportando le modifiche del caso, riprendiamo l'esempio visto in precedenza per i tipi
static e vediamo le differenze

Type TClasse1 = Class

Procedure Metodo1;

End;

TClasse2 = Class(TClasse1)

Procedure Metodo1; override;

End;

Var Classe1 : TClasse1;

Classe2 : TClasse2;

Begin

Classe1 := TClasse1.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

Classe1.Destroy;

Classe1 := TClasse2.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse2

TClasse2(Classe1).Metodo1;

Classe1.Destroy;

Classe2 := TClasse2.Create;

Classe2.Metodo1;
//L'implementazione utilizzata è quelle di TClasse2

Classe2.Destroy;

End;

Come ripostato nel commento del codice, la seconda chiamata a
"Classe1.Metodo1" utilizzerà l'implementazione di "TClasse2" poichè
il tipo run-time al momento della chiamata è "TClasse2".

Le differenze tra metodi virtuali e dinamici consiste essenzialmente
nell'implementazione dellq chiamata al metodo in fase di esecuzione. I metodi viruali sono
ottimizzati per velocità, mentre i metodi dinamici per dimensione.

Un'altro tipo interessante di metodo e quello dei metodi astratti. I metodi astratti
permettono di definire metodi senza alcuna implementazione del metodo stesso. Ciò risulta
utilie quando si vogliono creare delle classi base che stabiliscano l'interfaccia base dei
metodi ma che permettano alle classi discendenti di definire l'implementazione di quei
metodi. Questo tipo di metodo è un particolare tipo di metodo astratto o dinamico.

Per dichiarare un metoto astratto, occorre specificare dopo le parole chiave virtual
o dynamic la praola chiave abstract.

Altri due tipi di metodi importanti nella programmazione ad oggetti sono i metodi
costruttori e distruttori. Questi si occupano, come dicono le parole stesse, di creare ed
inizializzare un'istanza di classe o di distruggere e liberare la memoria occupata
dall'istanza di classe precedentemente creata.

I metodi costruttori sono caratterizzati dalla parola chiave constructor
al posto della parola chiave procedure o function.
L'implementazione di questi metodi, prevede la creazione dell'istanza di classe allocando
memoria nello heap, ed l'inizializzazione automatica a zero dei valori ordinal, a nil dei
campi puntatore e classe, l'azzeramento di tutti i campi string. Solitamente all'interno
di un metodo costruttore viene prima di tutto chiamato il metodo costruttore ereditato
dall'antenato per inizializzare il codice comune, dopo di che viene eseguito il codice
necessario ad inizializzare il codice specificao per la classe discendente. Potrebbe
capitare che durante l'esecuzione del codice di inizializzazione si generino delle
eccezioni; in tal caso verrà eseguito automaticamente il distruttore destroy per
disturggere l'oggetto non completato. Nella creazione di un oggetto, occorre chiamare il
metodo costruttore facendolo precedere dal nome della classe di appartenenza dell'oggetto
che si vuole creare. In tal modo, il metodo costruttore restituirà, al termine della
chiamata, un puntatore all'istanza appena creata. Se il metodo costruttore viene chiamato
utilizzando un riferimento ad un oggetto, questo non restituisce nulla.

I metodi distruttori seguono le stessa regola dichiarativa dei metodi costruttori
tranne per il fatto che utilizzano la parola chiave destructor al posto
di constructor. I metodi distruttori si occupano di distruggere l'oggetto
e deallocare la memoria da esso occupata. Per chiamare un metodo distruttore bisogna fare
riferimento ad un oggetto istanza come per esempio

MioOggetto.Destroy;

La chiamata al metodo destroy produce l'immediata esecuzione del codice
contenuto nell'implementazione del metodo destroy stesso. All'interno del metodo
distruttore viene eseguito tutto quel codice atto ad eliminare eventuali altre istanze di
oggetti incorporati e nella liberazione della memoria da loro occupata. L'ultima
istruzione di un metodo destroy , è solitamente una chiamata al metodo destroy
ereditato in maniera da eliminare i campi e ripulire la memoria occupata dal codice
ereditato. Come detto in precedenza, se durante la creazione di un oggetto viene sollevata
una eccezione, viene eseguito il metodo destroy. In tal caso, questo metodo deve
essere pronto ad interagire con codice non completamente inizializzato che, in particolare
per i campi di tipo class, significa controllare se il cmapo ha valore Nil
prima di effettuare la chiamata al metodo destroy della classe di quel campo. Per questo
Delphi mette a disposizione il metodo Free definito nella classe TObject
che permette di gestire in automatico il controllo sulla presenza di valori Nil
prima di eseguire la distruzione dell'oggetto.

Gestori di messaggi

Un altro tipo di metodo messo a disposizione in Delphi è il gestore di messaggi. Un
gestore di messaggi sono metodi che rispondono ai messaggi inviati da Windows, da altre
applicazioni o dall'applicazione che stiamo realizzando. Esistino messaggi predefiniti e
messaggi che possono essere dichiarati dallo sviluppatore.

Per dichiarare un gestore di messaggi, si include la direttiva message
dopo la dichiarazione del metodo seguito da un identificatore integer costante di
messaggio il cui valore deve essere compreso tra 1 e 49151. Alcuni dei messaggi utilizzati
da Windows e quelli utilizzati dai controlli della VCL sono definiti nella unit messages.
Nella definizione di ogni messaggio è necessario dichiarare una costante contenente l'ID
del messaggio ed una struttura (un record) che descriva il contenuto del messaggio. Ecco
la dichiarazione di un gestore di messaggio er il messaggio WM_CHAR

Type TTextBox = Class(TCustomControl)

Private

Procedure WMChar(Var Message : TWMChar); message
WM_CHAR;

...

End;

Il codice seguente, ripreso dal manuale del linguaggio, mostra come implementare il
gestore di evento dichiarato in precedenza affinchè venga eseguito un codice particolare
alla pressione del tasto invio in un controllo di tipo TTextBox e l'esecuzione del codice
ereditato per tutti gli altri tasti

Procedure TTextBox.WMChar(Var Message : TWMChar);

Begin

If Chr(Message.ChrCode) = #13 then

ProcessEnter

Else

Inherited;

End;

tramite l'istruzione Inherited viene ricercato nelle classi antenato una implementazione del gestore per quel messaggio e qualora non ne sia disponibile una, viene eseguito il metodo gestore predefinito DeafultHandler definito nella classe TObject. Per richiamare un gestore di messaggio, si utilizza il metodo Dispatch dichiarato nella classe TObject. Questo metodo richiede che gli venga passato un record il cui primo campo sia di tipo cardinal e contenga l'ID del messaggio.

Ti consigliamo anche