METODI COSTRUTTORI, DISTRUTTORI E OVERLOADING
La OOP mette a disposizione un tipo particolare di metodi che servono per la creazione dell’oggetto e l’eventuale inizializzazione dei suoi attributi : questi metodi prendono il nome di metodi costruttori.
METODO COSTRUTTORE
Un metodo costruttore è un metodo particolare, che si distingue dagli altri per tre elementi:
• ha lo stesso nome della classe;
• non ha tipo di ritorno, nemmeno void;
• ha visibilità public.
Vediamo due diversi metodi costruttore.
Non si deve indicare il tipo restituito!
ESEMPIO
Per aggiungere un metodo costruttore nella classe Frazione scriviamo un metodo con lo stesso nome della classe, come indicato di seguito. Oltre al costruttore di default, nella classe si inseriscono generalmente altri costruttori che però hanno uno o più parametri in ingresso, in modo che alla creazione dell’oggetto l’utilizzatore possa contemporaneamente assegnare dei valori ai sui attributi.
ESEMPIO – Frazione con due costruttori
Aggiungiamo un secondo costruttore alla classe Frazione che riceve in ingresso i valori da attribuire rispettivamente al numeratore e al denominatore.
Per realizzare un costruttore con parametri questi devono essere inseriti tra parentesi tonde, come viene fatto per le normali funzioni.
In questo caso il costruttore è stato inserito in modo inline automatico, scrivendo il suo codice all’interno della dichiarazione della classe.
Come possiamo osservare nel codice appena scritto, oltre che alla inizializzazione degli attributi, nel secondo costruttore viene anche richiamato un metodo : infatti il costruttore è a tutti gli effetti un metodo e quindi in esso si possono scrivere operazioni anche complesse e richiamare metodi della classe sull’oggetto che lui stesso ha appena creato.
OVERLOADING
La possibilità offerta dai linguaggi a oggetti di permettere la coesistenza di più metodi con lo stesso nome prende il nome di overloading.
Una classe può contenere più costruttori e metodi con lo stesso nome purché distinti da diverse intestazioni (signature), cioè:
Prendiamo per esempio la classe Contatore e ipotizziamo di avere due costruttori come i seguenti:
Contatore (int valore, int passo) Contatore (int passo, int valore)
Questi sono diversi solo apparentemente, ma sono identici formalmente.
Infatti, se effettuiamo due ipotesi di chiamata come le seguenti : Contatore (0, 1)
Contatore (1, 0)
Quale dei due costruttori viene eseguito? L’ambiguità è evidente.
Quindi è necessario che sia diverso il numero oppure il tipo dei parametri.
Quindi questi sei metodi costruttori possono coesistere in una classe e il programmatore può scegliere quale utilizzare al momento della creazione degli oggetti, secondo la propria necessità.
DISTRUTTORE
Il distruttore serve per effettuare tutte quelle azioni di “pulizia” che risultano necessarie prima della deallocazione dell’oggetto, ovvero:
• deallocare i dati dinamici che sono stati allocati da funzioni membro dell’oggetto;
• chiudere i file che sono stati aperti dalle funzioni membro dell’oggetto.
Nelle classi semplici esiste un distruttore standard che viene automaticamente chiamato dal sistema ogniqualvolta un oggetto viene deallocato, cioè se:
• l’oggetto è locale e si esce dal blocco in cui è dichiarato;
• l’oggetto è nella heap (memoria dinamica) e viene rimosso il riferimento a esso;
• l’oggetto è un membro di un altro oggetto e quest’ultimo viene deallocato.
Nel codice viene effettuato direttamente con le seguenti istruzioni:
Il simbolo che individua il metodo distruttore è indicato di seguito :
CREAZIONE DI OGGETTI
La creazione di un oggetto avviene mediante la seguente istruzione:
Contatore conta1,conta2;
Oggetti allocati nella memoria dinamica (nella heap)
Vi è la possibilità di dichiarare l’oggetto come variabile dinamica, con l’istruzione new : in questo caso il programma non definisce direttamente un oggetto, ma un suo puntatore, cioè una variabile che contiene l’indirizzo di memoria di partenza dove questo viene memorizzato.
Definiamo un oggetto contatore :
Contatore * puntaConta1; // crea una “variabile puntatore “ a un oggetto Contatore puntaConta1=new Contatore; //costruisce l’area puntata
La seconda istruzione esegue varie cose in una sola volta:
• alloca memoria dinamica per un oggetto della classe contatore;
• assegna l’indirizzo dell’oggetto, restituito dall’operatore new, al puntatore puntaConta1;
• inizializza l’oggetto eseguendo il costruttore di default della classe Contatore.
Se volessimo invocare un costruttore diverso da quello di default basterebbe indicare tra parentesi tonde i parametri che si desidera passare :
Contatore * puntaConta1; // crea una “variabile puntatore “ a un oggetto Contatore
puntaConta1=new Contatore(5); //costruisce l’area puntata e inizializza a 5 il valore iniziale di conteggio ESEMPIO