User Tools

Site Tools


appunti3s:references

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

appunti3s:references [2018/04/25 07:55]
appunti3s:references [2020/06/08 22:20] (current)
Line 1: Line 1:
 +====== Premessa per chi conosce i puntatori======
 +NOTA BENE: in questo capitolo mancano di proposito il concetto di puntatore e di variabile globale: questa premessa è solo per chi già li conoscesse. Chi non conosce i puntatori deve saltare questo paragrafo e passare a quello sottostante
 +
 +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 [[appunti3s:programmazione_procedurale#passaggio_per_valore]].
 +Questo impedisce di effettuare delle modifiche sui //parametri effettivi// della funzione.
 +<code>int triplicare(int x)
 +{
 + x=x*3; // modifica solo la var. locale x
 +        // il risultato deve essere restituito
 + return x;
 +}
 +</code>
 +
 +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 //parametri effettivi//
 +<code>int triplicare(int* p)
 +{
 + *p=*p*3;  // modifica anche la variabile puntata da p
 + return 0; // non e' necessario restituire il risultato
 +}
 +</code>
 +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 praticamente la stessa "forza" dei puntatori, ma una sintassi molto più semplice.
 +
 +====== References ======
 +
 +Quando una funzione ha come //parametro formale// un reference, al suo interno NON viene creata una nuova variabile locale, ma si lavora proprio sul //parametro effettivo// usando il nome del parametro formale come il suo soprannome temporaneo.
 +
 +<code>void calcolaTriplo(int& x); </code>
 +La dichiarazione si legge in questo modo:
 +la funzione accetta come argomento un //reference a...// intero.
 +La chiamata della funzione, invece, è come quella vista nel passaggio per valore...
 +<code>calcolaTriplo(mioNumero); </code>
 +
 +Nella seguente figura le funzioni main() e calcolaTriplo() sono rappresentate da due contenitori. la variabile //x// non ha una propria area di memoria, ma è solo il soprannome (alias) della variabile mioNumero. //mioNumero// è una variabile locale di main() e non si può usare il suo nome in calcolaTriplo() ma è possibile usare il termine //x// per fare la stessa cosa.
 +
 +{{ :appunti3s:perreference.png |}}
 +===== Un solo simbolo con molti significati =====
 +
 +Purtroppo il simbolo //&//, usato in un altro luogo, possiede anche altri significati, completamente diversi. Dentro un'espressione logica può significare operatore "//AND binario//", oppure, scritto davanti al nome di una variabile, diventa l'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.
 +
 +<file cpp 22.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 calcolaTriplo(int& x) 
 +{
 +  x = 3*x;
 +}
 +
 +int main ()
 +{
 +  int mioNumero;
 +
 +  std::cout << "Per favore scrivi un numero intero: ";
 +  std::cin >> mioNumero;
 +  
 +  calcolaTriplo(mioNumero); // chiamata della funzione che modifica il mioNumero
 +  
 +  std::cout << "il triplo vale " << mioNumero << ".\n";
 +  return 0;
 +}
 +</file>
 +
 +===== 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" (che potrebbe essere difficile da copiare o di notevoli dimensioni). 
 +
 +<file cpp 23.cpp>
 +// Questo programma è stato scritto da Fabio.
 +
 +#include <iostream>
 +
 +// questo modo di passare il parametro permette di agire sul parametro effettivo
 +// NON passa una copia
 +// il termine const evita modifiche indesiderate al parametro passato.
 +// NON crea una nuova copia della variabile, si risparmia memoria!
 +// x è solo il soprannome temporaneo che viene dato a mioNumero
 +
 +int calcolaTriplo(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 " << calcolaTriplo(mioNumero) ".\n";
 +  return 0;
 +}
 +</file>
 +Per comprendere il risparmio di memoria ottenuto con i references costanti, confrontare la seguente tabella di traccia con quella ottenuta con il [[appunti3s:programmazione_procedurale&#visibilità delle variabili|passaggio per valore]]. Quante colonne ci sono nelle diverse tabelle?
 +
 +^mioNumero (alias x) ^ output ^
 +|  6  |  -  |
 +|  6  |  -  |
 +|  6  |  18  |
 +
 +
 +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 per evitare errori.
 +Basta aggiungere il modificatore //const// in fondo della dichiarazione della funzione (prima del punto e virgola). Questo impedisce modifiche accidentali ai dati. 
 +Il compilatore controllerà che questa funzione non esegua alcuna istruzione di modifica su NESSUNO dei dati passati. Se il programmatore non rispetta questa regola, verrà avvisato dal compilatore. Per questo motivo //const// dovrebbe essere usato su tutte le funzioni di tipo //ispettrici// (vedere [[appunti3s:programmazione_procedurale#classificazione_delle_funzioni]])
 +
 +Le funzioni costanti (quindi, ispettrici) sono anche le uniche che possono avere come argomenti interi oggetti costanti come:
 +
 +  * fun(const int& x) const
 +  * fun(const Classe unOggetto) const
 +  * fun(const Classe& unOggetto) const
 +  * fun(const Classe* unOggetto) const
 +Infatti, provando a passare un oggetto __costante__ ad una funzione //non costante// si ottiene errore. 
 +  * fun(const Classe unOggetto)  %%//%% ERRORE
 +Questo perché una tale funzione (non ispettrice, ma modificatrice) dovrebbe consentire di modificare l'oggetto. 
 +
 +====Errore da evitare====
 +Restituire un reference (return) è un'istruzione legale ma a volte non è corretta perché può generare un problema di allocazione della memoria, in quanto le variabili locali di una funzione vengono distrutte al suo termine.
 +
 +Per approfondire si può studiare il seguente esempio, dove l'uso dei reference costanti è corretto.
 +
 +<file cpp 24.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)
 +// ma e' molto facile commettere errori...
 +
 +#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;
 +}
 +</file>
 +
 +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. 
 +In questo caso restituire un reference è corretto perché l'oggetto non è locale, altrimenti questo sarebbe stato distrutto... 
 +
 +===== Esempi =====
 +I reference sono un nuovo tipo del C++ quindi, tutte le funzioni che usano il tipo reference sono utilizzate sempre con la dot notation...
 +(Fare prima la programmazione orientata agli oggetti e DOPO i reference?)
 +
 +  - int compare ( const string& str ) const; 
 +  - int find ( char c, size_t pos = 0 ) const;