Passaggio di parametri e argomenti di default
I parametri di una funzione si comportano all'interno del corpo della funzione
come delle variabili locali e possono quindi essere usati anche a sinistra di
un assegnamento (per quanto riguarda le variabili locali ad una funzione, si
rimanda al capitolo III, paragrafo 3):
void Assign(int a, int b) {
a = b; // Tutto
OK, operazione lecita!
}
tuttavia qualsiasi modifica ai parametri formali (quelli cioe` che compaiono
nella definizione, nel nostro caso a e b ) non si riflette
(per quanto visto fin'ora) automaticamente sui parametri attuali (quelli effettivamente
usati in una chiamata della funzione):
#include < iostream >
using namespace std;
void Assign(int a, int b) {
cout << "Inizio Assign, parametro a = " <<
a << endl;
a = b;
cout << "Fine Assign, parametro a = " << a
<< endl;
}
int main(int, char* []) {
int X = 5;
int Y = 10;
cout << "X = " << X << endl;
cout << "Y = " << Y << endl;
// Chiamata della funzione Assign
// con parametri attuali X e Y
Assign(X, Y);
cout << "X = " << X << endl;
cout << "Y = " << Y << endl;
return 0;
}
L'esempio appena visto e` perfettamente funzionante e se eseguito mostrerebbe
come la funzione Assign , pur eseguendo una modifica ai suoi parametri
formali, non modifichi i parametri attuali. Questo comportamento e` perfettamente
corretto in quanto i parametri attuali vengono passati per valore: ad ogni chiamata
della funzione viene cioe` creata una copia di ogni parametro localmente alla
funzione stessa; tali copie vengono distrutte quando la chiamata della funzione
termina ed il loro contenuto non viene copiato nelle eventuali variabili usate
come parametri attuali.
In alcuni casi tuttavia puo` essere necessario fare in modo che la funzione
possa modificare i suoi parametri attuali, in questo caso e` necessario passare
non una copia, ma un riferimento o un puntatore
e agire su questo per modificare una variabile non locale alla funzione. Per
adesso non considereremo queste due possibilita`, ma rimanderemo la cosa al
capitolo successivo non appena avremo parlato di puntatori e reference.
A volte siamo interessati a funzioni il cui comportamento e` pienamente definito
anche quando in una chiamata non tutti i parametri sono specificati, vogliamo
cioe` essere in grado di avere degli argomenti che assumano un valore di default
se per essi non viene specificato alcun valore all'atto della chiamata. Ecco
come fare:
int Sum (int a = 0, int b = 0) {
return a+b;
}
Quella che abbiamo appena visto e` la definizione della funzione Sum
ai cui argomenti sono stati associati dei valori di default (in questo caso
0 per entrambi gli argomenti), ora se la funzione Sum viene chiamata
senza specificare il valore di a e/o b il compilatore
genera una chiamata a Sum sostituendo il valore di default (0)
al parametro non specificato. Una funzione puo` avere piu` argomenti di default,
ma le regole del C++ impongono che tali argomenti siano specificati alla fine
della lista dei parametri formali nella dichiarazione della funzione:
void Foo(int a, char b = 'a') {
/* ... */
} // Ok!
void Foo2(int a, int c = 4, float f) {
/* ... */
} // Errore!
void Foo3(int a, float f, int c = 4) {
/* ... */
} // Ok!
La dichiarazione di Foo2 e` errata poiche` quando viene specificato
un argomento con valore di default, tutti gli argomenti seguenti (in questo
caso f ) devono possedere un valore di default; l'ultima definizione
mostra come si sarebbe dovuto definire Foo2 per non ottenere errori.
La risoluzione di una chiamata di una funzione con argomenti di default naturalmente
differisce da quella di una funzione senza argomenti di default in quanto sono
necessari un numero di controlli maggiori; sostanzialmente se nella chiamata
per ogni parametro formale viene specificato un parametro attuale, allora il
valore di ogni parametro attuale viene copiato nel corrispondente parametro
formale sovrascrivendo eventuali valori di default; se invece qualche parametro
non viene specificato, quelli forniti specificano il valore dei parametri formali
secondo la loro posizione e per i rimanenti parametri formali viene utilizzato
il valore di default specificato (se nessun valore di default e` stato specificato,
viene generato un errore):
// riferendo alle precedenti definizioni:
Foo(1, 'b'); // chiama
Foo con argomenti 1 e 'b'
Foo(0); //
chiama Foo con argomenti 0 e 'a'
Foo('c'); // ?????
Foo3(0); // Errore,
mancano parametri!
Foo3(1, 0.0); // chiama Foo3(1,
0.0, 4)
Foo3(1, 1.4, 5); // chiama Foo3(1, 1.4, 5)
Degli esempi appena fatti, il quarto, Foo3(0) , e` un errore poiche`
non viene specificato il valore per il secondo argomento della funzione (che
non possiede un valore di default); e` invece interessante il terzo (Foo('c'); ):
apparentemente potrebbe sembrare un errore, in realta` quello che il compilatore
fa e` convertire il parametro attuale 'c ' di tipo char
in uno di tipo int e chiamare la funzione sostituendo al primo
parametro il risultato della conversione di 'c ' al tipo int .
La conversione di tipo sara` oggetto di una apposita appendice.
|