Premessa

Sia nel linguaggio C che in C++ si può effettuare il passaggio di parametri esclusivamente per valore, cioè copiando il contenuto delle variabili indicate tra parentesi. Vedere esempio passaggio_per_valore. Questo impedisce di effettuare delle modifiche sui parametri effettivi della funzione.

int triplicare(int x)
{
 x=x*3; // modifica solo la var. locale x
 return x;
}

Tuttavia, se al posto dei tradizionali tipi di dato (come int) si usano i puntatori (come int*), la funzione riesce ad accedere e a modificare anche i parametro effettivi.

int triplicare(int* p)
{
 *p=*p*3;  // modifica anche la variabile puntata da p
 return *p;
}

Come si può notare dal secondo esempio, questo appesantisce il codice costringendo ad aggiungere l'operatore di deferimento * (l'asterisco) davanti al puntatore p.

I references, invece, hanno la stessa “forza” dei puntatori, ma una sintassi molto semplice.

References

Quando una funzione ha come parametro formale un reference, al suo interno NON viene crea una nuova variabile locale, ma si lavora proprio sul parametro effettivo usando il parametro formale come il suo soprannome temporaneo.

void triplicare(int& x); 

La dichiarazione si legge in questo modo: la funzione accetta come argomento un reference a… intero.

Purtroppo il simbolo &, usato in un altro luogo, possiede anche altri significati, completamente diversi. Dentro un'espressione logica può significare operatore AND binario, oppure scirtto davanti al nome di una variabile, operatore indirizzo di. Il reference al momento della sua dichiarazione deve essere sempre inizializzato, infatti non si può usare un soprannome senza dire a chi corrisponde quell'alias… Al contrario dei puntatori, i reference, una volta inizializzati come alias ad un oggetto, non possono diventare l'alias di niente altro.

20.cpp
// Questo programma è stato scritto da Fabio.
// Serve a calcolare il triplo di un numero.
 
#include <iostream>
 
// questo modo di passare il parametro 
// non crea una copia locale
// questa funzione e' di tipo void, cioe' non restituisce nulla (manca return)
 
void triplicare(int& x) 
{
  x = 3*x;
}
 
int main ()
{
  int mioNumero,mioTriplo;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  triplicare(mioNumero); // chiamata della funzione che modifica il mioNumero
 
  std::cout << "il triplo vale " << mioNumero << ".\n";
  return 0;
}

references costanti

Premettendo const davanti ad un reference si impedisce di modificare il parametro effettivo. L'effetto è simile al classico passaggio per valore, ma con la differenza che invece di creare una copia del parametro effettivo, in pratica si copia solo il suo indirizzo.

Questo è utilizzato soprattutto quando il parametro effettivo è un “oggetto” (difficile da copiare o di notevoli dimensioni).

21.cpp
// Questo programma è stato scritto da Fabio.
 
#include <iostream>
 
// questo modo di passare il parametro 
// passa l'indirizzo di memoria di questo parametro
// il termine const evita modifiche indesiderate al parametro.
 
// NON creando una nuova variabile x, risparmio memoria!
// x è solo il soprannome temporaneo che viene dato a mioNumero
 
int triplicare(const int& x)  
{
 return 3*x;
}
 
int main ()
{
  int mioNumero;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  std::cout << "il triplo di " << mioNumero
            << " vale " << triplicare(mioNumero) ".\n";
  return 0;
}
22.cpp
// Questo programma è stato scritto da Fabio.
 
// Serve a calcolare il triplo di un numero.
// I reference sono utili per risparmiare memoria
// anche sul tipo restituito dalla funzione
// basta aggiungere UN SECONDO reference (sempre preceduto da un const)
 
#include <iostream>
 
const int& triplicare(const int& x)  
{
 return 3*x;
}
 
int main ()
{
  int mioNumero;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  std::cout << "il triplo di " << mioNumero
            << " vale " << triplicare(mioNumero) ".\n";
  return 0;
}

Il nuovo alias davanti alla funzione evita di creare una nuova variabile temporanea che sarebbe necessaria anche per restituire il valore con return. Il nuovo const davanti alla funzione impedisce modifiche indesiderate a tale oggetto locale.

I const reference sono anche l'unico modo con cui si possono utilizzare gli oggetti temporanei inaccessibili (come le espressioni costanti o gli oggetti temporanei restituiti da una funzione)

vedere Thinking in C++, capitolo 11: References & the Copy-Constructor pag.477

Funzioni costanti

Le funzioni costanti sono importanti perché sono un buono strumento di correzione degli errori. Basta aggiungere un modificatore const in fondo (alla fine) della firma della funzione. Questo evita modifiche accidentali ai dati degli oggetti

Basta scrivere const in fondo al nome della funzione per costringere il compilatore a fare un controllo che quella funzione non esegua alcuna istruzione di modifica su TUTTI i membri dato di quell'oggetto. Per questo motivo dovrebbe essere usato su tutte le funzioni di tipo ispettore (vedi classificazione delle funzioni)

Le funzioni di questo tipo sono anche le uniche che possono avere come argomenti interi oggetti costanti come:

  • const Classe unOggetto
  • const& Classe unOggetto
  • const Classe* unOggetto
  • appunti3s/cenni_ai_references.txt
  • Last modified: 2018/04/25 07:55
  • (external edit)