Nel C++ scrivere il nome di una funzione, seguita dalle parentesi tonde, equivale a chiamare quella funzione e a ricevere in uscita il valore di ritorno di quella funzione:
calcolaSomma(93, 418);
//oppure nel caso di una funzione che non richiede argomenti:
stampaFirma();
Invece scrivere il nome della funzione senza le parentesi tonde ci permette di ottenere il suo indirizzo:
calcolaSomma;
La dichiarazione di un puntatore a funzione invece, è un po' diversa dalla dichiarazione di un puntatore a variabile:
int calcolaSomma(int, int); //prototipo della funzione da puntare
int (*puntaFunzione)(int, int); //puntatore a funzione
puntaFunzione = calcolaSomma; //assegna a puntaFunzione l'indirizzo della funzione calcolaSomma
Nella dichiarazione di un puntatore a funzione bisogna specificare il tipo di ritorno della funzione a cui punteremo, ed i tipi dei suoi parametri.
Inoltre è necessario racchiudere il puntatore a funzione tra parentesi tonde, altrimenti il compilatore lo interpreterà come il prototipo di una normale funzione che restituisce un puntatore ad intero:
int* puntaFunzione(int, int); //ERRATO! non è un puntatore
Infatti se non mettiamo il nome del puntatore tra parentesi tonde, l'asterisco sarà attribuito al tipo di ritorno, e verrà interpretato come "crea una funzione che restituisce un puntatore a int".
Sappiamo che, nelle comuni variabili, per accedere al contenuto dell'area puntata dal puntatore, dovremo utilizzare l'operatore di indirezione *:
cout << puntatore; //stampa l'indirizzo puntato da puntatore
cout << *puntatore; //stampa il contenuto dell'area puntata da puntatore
Lo stesso avviene con i puntatori a funzione, tranne l'accorgimento di racchiudere tra parentesi tonde il nome del puntatore:
(*puntaFunzione)(12, 98); //richiama la funzione a cui punta puntaFunzione
Questa riga di codice non fa altro che richiamare la funzione contenuta nell'area di memoria puntata da puntaFunzione, e cioé calcolaSomma();
Un puntatore a funzione può essere utile quando una funzione richiede un'altra funzione come parametro.
Ad esempio ammettiamo di avere tre funzioni, di cui scriviamo i rispettivi prototipi:
int calcolaSomma(int, int);
int calcolaSottrazione(int, int);
int calcolaMoltiplicazione(int, int);
Come possiamo vedere le tre funzioni hanno lo stesso tipo di ritorno che lo stesso numero e gli stessi tipi di parametri.
Differiscono infatti solo nel nome e nelle operazioni che svolgono al loro interno.
Mettiamo ora di avere un'altra funzione, che prende un numero intero in ingresso.
Come abbiamo imparato una funzione può ricervere come parametri altre funzioni, il che equivale a ricevere il valore restituito dalle funzioni passate come parametro:
int funzioneBoh(int); //riceve in ingresso un intero
funzioneBoh( calcolaSomma(5, 4) ); //riceve in ingresso il valore restituito da calcolaSomma()
Ora, mettiamo che una funzione abbia bisogno di richiamare al suo interno una funzione esterna, ma la funzione da chiamare non può essere decisa durante la scrittura del programma.
La funzione può cioé aver bisogno di una funzione esterna diversa in base alle circostanze.
Ad esempio dobbiamo scrivere una funzione del nostro videogioco epico, che si occupa di gestire l'immagine del nostro personaggio principale.
Ad esempio il personaggio può indossare un cappello, o una bandana, o ancora delle borchie o una cravatta, ma questo dipende da quali item raccoglierà il giocatore durante la sua sessione di gioco, cosa che non può essere conosciuta in anticipo dal programmatore.
Mettiamo di avere varie funzioni grafiche che si occupano di disegnare a schermo un dato elemento, e restituiscono in uscita l'istanza di una struttura di tipo Texture:
Texture cappello();
Texture borchie();
Texture cintura();
Texture mantello();
Ora abbiamo la nostra funzione che disegna il personaggio:
void disegnaPg()Come vedete in questo modo siamo obbligati a decidere staticamente cosa indosserà il nostro personaggio.
{
//...codice...
cintura(); //richiama la funzione per disegnare la cintura
//...codice....
}
Se invece la nostra funzione avesse come parametro un puntatore a funzione?
Le funzioni che disegnano i vari accessori sono tutte uguali, restituiscono tutte lo stesso tipo Texture e non ricevono argomenti in ingresso.
void disegnaPg( Texture (*ptFunzione)() )Come vedete la nostra funzione ha come parametro un puntatore a funzione.
{
//...codice...
(*ptFunzione)(); //richiama la funzione per disegnare la cintura
//...codice....
}
Questo significa che potrà ricevere come argomento una qualsiasi funzione coerente con il puntatore.
All'interno del blocco di codice poi, basterà utilizzare il puntatore a funzione per utilizzare la funzione passata. Il puntatore è lo stesso, ma la funziona chiamata all'interno del codice dipenderà da quale funzione verrà passata come argomento a disegnaPg.
Ad esempio potremo avere all'interno del nostro codice sorgente una parte che gestisce la collisione tra il nostro personaggio e degli oggetti bonus che si trovano per terra nei livelli.
Quando il giocatore ad esempio raccogliarà un cappello, alla nostra funzione verrà passato come argomento la funzione cappello():
if(giocatore.posizione == cappello.posizione) //se il giocatore raccoglie un cappelloIn entrambi i casi viene chiamata la funzione disegnaPg(), ma nel primo caso il puntatore (*ptFunzione)() punterà a cappello(), mentre nel secondo caso punterà a cintura().
{
disegnaPg(cappello); //richiama la funzione disegnaPg a cui passa la funzione disegnaCappello
}
else if(giocatore.posizione == cintura.posizione)
{
disegnaPg(cintura); //richiama la stessa funzione disegnaPg ma questa volta le passa la funzione cintura()
}
All'interno della nostra funzione disegnaPg() verrà utilizzato sempre lo stesso puntatore (*ptFunzione)(), ma nel primo caso richiamerà la funzione cappello(), mentre nel secondo caso richiamerà la funzione cintura().
Nessun commento:
Posta un commento