Menu

Parametri delle funzioni in C++: per valore, riferimento e const

Come arrivano gli argomenti alle funzioni C++: passaggio per valore e per riferimento, riferimenti const per un accesso economico in sola lettura, argomenti predefiniti, puntatori e le insidie delle copie che rallentano i programmi in silenzio.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Parametri e argomenti

I parametri di una funzione sono le variabili con un nome nella sua definizione; gli argomenti sono i valori concreti che le passi quando la chiami. La pagina precedente mostrava come definire e chiamare le funzioni; questa pagina riguarda il modo in cui quei valori entrano davvero, perché il C++ ti offre diverse possibilità e la scelta influisce sia sulla correttezza sia sulla velocità.

Il comportamento predefinito in C++ è il passaggio per valore: la funzione riceve una copia.

Dentro addTen, n è una variabile separata inizializzata a partire da score. Riassegnare n tocca solo quella copia, quindi in main score resta intatto. È sicuro e prevedibile, perché la funzione non può rovinarti i dati per sbaglio, ed è proprio per questo che è il comportamento predefinito.

Passaggio per riferimento: lasciare che una funzione modifichi il chiamante

A volte vuoi che la funzione modifichi la variabile del chiamante. Aggiungi un & al tipo del parametro e diventa un riferimento, cioè un alias dell'originale, non una copia:

L'unica differenza rispetto al primo esempio è il &, ma ora n e score sono lo stesso oggetto. È il modo standard per "restituire" più di un valore o per aggiornare qualcosa sul posto. Un uso classico è scambiare due variabili:

Senza il &, swapValues scambierebbe due copie e main non vedrebbe alcun cambiamento: un bug molto comune tra i principianti.

Riferimenti const: accesso economico in sola lettura

Il passaggio per valore copia l'argomento. Per un int non costa nulla, ma copiare una string o un vector grandi a ogni chiamata è lavoro reale e sprecato. La soluzione è un riferimento const (const T&): ottieni la velocità di un riferimento (nessuna copia) più la promessa, fatta rispettare dal compilatore, di non modificare l'argomento.

Una regola pratica utile: passa i tipi predefiniti piccoli (int, double, char, bool, puntatori) per valore, e gli oggetti grandi che devi solo leggere per riferimento const. Riserva un semplice T& non const ai casi in cui vuoi davvero modificare l'oggetto del chiamante.

Un'insidia sottile: un semplice int& n non può legarsi a un temporaneo o a un letterale. addTen(5) del primo esempio non compilerebbe se il parametro fosse int&, perché 5 non è una variabile di cui puoi creare un alias. Un const int& può legarsi a 5, un motivo in più per cui i riferimenti const sono così diffusi.

Argomenti predefiniti

Puoi dare a un parametro un valore di riserva, così i chiamanti possono ometterlo. Se l'argomento manca, viene usato il valore predefinito:

Due regole mettono in difficoltà. Primo, i valori predefiniti devono stare in fondo: quando un parametro ha un valore predefinito, devono averlo anche tutti quelli che lo seguono. Non puoi scrivere void f(int a = 1, int b), perché non ci sarebbe modo di fornire b saltando a. Secondo, quando una funzione è dichiarata in un header e definita altrove, metti il valore predefinito solo nella dichiarazione e non ripeterlo mai nella definizione: ripeterlo è un errore di compilazione.

Passare array e vector

Un array classico decade a puntatore quando viene passato, quindi la funzione perde traccia della sua dimensione: quasi sempre passi anche la lunghezza.

Dato che l'array è diventato un puntatore, sizeof(arr) dentro sum restituirebbe la dimensione di un puntatore, non dell'array: un bug famigerato. Nel C++ moderno preferisci uno std::vector (o std::span in C++20), passato per riferimento const, che porta con sé la propria dimensione:

Nota il const&: se lo togli, ogni chiamata copia l'intero vector. Per un vector di quattro elementi è innocuo, ma con un milione di elementi è un pozzo silenzioso di prestazioni.

Parametri puntatore

Puoi anche passare un puntatore (T*). Come un riferimento, permette alla funzione di raggiungere i dati del chiamante, ma un puntatore può essere spostato su un altro oggetto o essere nullo, quindi è lo strumento giusto quando "nessun valore" è un'opzione legittima:

Il chiamante passa &value per condividere il proprio indirizzo, e la funzione scrive attraverso *out. La differenza chiave rispetto ai riferimenti: un puntatore potrebbe essere nullptr, quindi una funzione che lo riceve dovrebbe controllarlo prima di dereferenziarlo. Saltare quel controllo e dereferenziare un puntatore nullo è comportamento indefinito, di solito un crash. Se "nessun valore" non ha mai senso, un riferimento è più pulito, perché fin dall'inizio non può essere nullo.

Prossimo: Riferimenti

I parametri sono il posto in cui i riferimenti danno il meglio, ma i riferimenti sono una funzionalità a sé stante: alias che puoi creare per qualsiasi variabile, non solo nella firma di una funzione. La prossima pagina approfondisce come funzionano i riferimenti da soli: come dichiararli, perché vanno inizializzati subito, la differenza tra un riferimento lvalue e un riferimento const, e i modi subdoli in cui un riferimento può finire per essere pendente.

Domande frequenti

Qual è la differenza tra passaggio per valore e per riferimento in C++?

Il passaggio per valore copia l'argomento nel parametro, quindi le modifiche dentro la funzione non toccano il chiamante. Il passaggio per riferimento (int&) rende il parametro un alias della variabile del chiamante, quindi le modifiche sono visibili anche fuori. Usa void f(int x) per copiare e void f(int& x) per modificare l'originale.

Quando usare un parametro riferimento const in C++?

Usa const T& quando vuoi leggere un oggetto grande senza copiarlo e senza permettere alla funzione di modificarlo, per esempio void print(const string& s). Ti dà la velocità del passaggio per riferimento con la sicurezza del passaggio per valore. Per tipi piccoli come int o char, il semplice passaggio per valore è altrettanto veloce.

Cosa sono gli argomenti predefiniti in C++?

Gli argomenti predefiniti permettono a un parametro di assumere un valore di riserva quando il chiamante lo omette, per esempio void greet(string name = "there"). I valori predefiniti devono stare sugli ultimi parametri (quelli più a destra), e li indichi solo nella dichiarazione, non nella definizione, se le due sono separate.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA