Allโatto della scrittura di un programma, anche molto semplice, ci si ritrova spesso a fare uso di funzioni. Per funzione si intende una parte di codice identificata da un nome e da una serie di parametri, che puรฒ essere richiamata in varie parti del programma.
Grazie allโutilizzo di funzioni una stessa porzione di codice, utilizzata in piรน parti del programma, non deve essere riscritta piรน volte: sarร infatti possibile scrivere la parte di codice in comune allโinterno di una funzione che potrร essere richiamata quando necessario.
Ad esempio si potrebbe definire una funzione stampa che visualizzi a video un certo numero di parametri di interesse e richiamarla poi con una semplice riga di codice (il nome della funzione) ogni volta che, nel programma principale, sia necessario avviare la visualizzazione di tali dati. Si migliora in tal modo sia lโefficienza nella programmazione sia la leggibilitร del codice.
Poniamo il caso perรฒ che si abbia a che fare con situazioni in cui si richiedono funzioni con funzionalitร analoghe ma formalmente diverse.
Riprendiamo lโesempio giร esposto in precedenza: supponiamo che la nostra ipotetica funzione di stampa abbia lo scopo di stampare a video tutti i records estratti da una data tabella del nostro database; si ipotizzi, ancora, che in talune altre circostanza si desideri estrarre solo un numero determinato di records.
La soluzione piรน immediata sarebbe, senzโaltro, quella di scrivere due funzioni distinte, caratterizzate dunque da nomi diversi. Tale scelta non risulta perรฒ brillante dal punto di vista della leggibilitร del codice, considerando che entrambe le funzioni svolgono, a livello concettuale, un ruolo del tutto analogo (estrarre dati dal database).
Sarebbe possibile anche sviluppare unโunica funzione in grado di diversificare il proprio operato sulla base da due numeri interi passati in argomento: il primo indicherebbe il tipo di lavoro da svolgere (ad esempio 0 per stampare lโintera tabella, 1 per stamparne una parte) ed il secondo il numero di righe da stampare in caso si stampi una parte della tabella (in caso contrario il valore del secondo parametro sarebbe ininfluente). Da un punto di vista funzionale questa soluzione รจ ineccepibile, ma da un punto di vista logico credo sia una soluzione concettualmente debole in quanto si ricorre a parametri fittizi e non necessari.
Allora perchรจ invece non sviluppare due funzioni identificate dallo stesso nome, una senza parametri (stampa lโintera tabella) e una con un parametro intero (stampa un certo numero di righe)?
Tale possibilitร รจ garantita dal meccanismo di overloading, che consente di derogare alla regola secondo cui una funzione debba essere identificata da un nome univoco, garantendo la possibilitร di distinguere a quale delle omonime funzioni debba farsi ricorso semplicemente facendo riferimento al numero ed alla tipologia di parametri forniti.
Ad esempio ipotizziamo di scrivere un codice che contenga le seguenti definizioni di funzione:
int somma(int addendo1,int addendo2)
int somma(int addendo1,int addendo2,int addendo3)
Eโ possibile definire separatamente il corpo delle due funzioni e richiamare quella piรน opportuna con una chiamata a somma accompagnata da due parametri interi per la prima funzione, tre per la seconda.
Definito in generale il meccanismo di overloading, รจ interessante notare come il linguaggio C++ offra unโulteriore interessante opportunitร : lโoverloading degli operatori.
Eโ possibile cioรจ associare allโutilizzo di un operatore, se legato ad operazioni relative ad una certa classe, una particolare sequenza di istruzioni definita nel corpo della classe stessa.
Ad esempio, se si volesse progettare una classe Counter con funzioni di contatore potrebbe essere utile (oltre alla definizione, ad esempio, dei costruttori, del distruttore, del costruttore di copia e di un metodo per incrementare il valore del dato membro della classe) anche far sรฌ che tale incremento possa essere realizzato tramite lโuso dellโoperatore unario ++. In altre parole si vuol rendere possibile e corretta, nel programma principale, una sequenza del tipo:
Counter count=new Counter(0);
count++;
con corrispondente incremento del valore del dato membro val di count.
Per ottenere tale risultato รจ necessario dichiarare allโinterno dei metodi della classe Counter un particolare metodo denominato, in questo caso, void operator++, ad esempio in questo modo:
class Counter{
unsigned short val;
...
public:
...
void operator++()
}
A livello di implementazione, seguirร la descrizione delle azioni da associare allโoperatore ++:
void Counter::operator++()
{
val++;
}
Lโoverloading degli operatori risulta utile soprattutto nel caso in cui lโutilizzo di un operatore risulti particolarmente intuitivo per unโoperazione di uso comune tra oggetti del tipo che si sta definendo in fase di progettazione della classe (ad esempio puรฒ essere di grande praticitร lโoverloading dellโoperatore binario + per la concatenazione di stringhe di testo o simile).
Questa possibilitร รจ utile anche qualora si desideri sovraccaricare gli operatori matematici al fine di mantenere il loro significato comune anche in contesti matematici differenti. Si pensi, ad esempio, allโutilizzo dellโoperatore + per lavorare con i vettori (si pensi, per semplicitร , a vettori costituiti da due soli elementi), utilizzo che puรฒ essere opportunamente ridefinito grazie allโoverloading, facendo sรฌ che la somma di due vettori abbia come risultato un vettore con componenti uguali ciascuna alla somma delle componenti dei due addendi. Ad esempio:
class Vector{
int x;
int y;
...
public:
...
Vector operator +(Vector v);
}
...
Vector Vector::operator +(Vector v)
{
Vector temp=new Vector();
temp.x=this.x+v.getx();
temp.y=this.y+v.gety();
return temp;
}