C ++: passaggio di argomenti “passati per riferimento”

Capisco come con qualsiasi altra variabile, il tipo di parametro determina l’interazione tra il parametro e il suo argomento. La mia domanda è: qual è il motivo dietro il quale si dovrebbe fare riferimento a un parametro e perché non lo si dovrebbe fare? Perché alcuni parametri delle funzioni fanno riferimento e altri no? Hai difficoltà a capire i vantaggi di farlo, qualcuno potrebbe spiegare?

La capacità di passare per riferimento esiste per due motivi:

  1. Per modificare il valore degli argomenti della funzione
  2. Per evitare di fare copie di un object per motivi di prestazioni

Esempio per modificare l’argomento

void get5and6(int *f, int *s) // using pointers { *f = 5; *s = 6; } 

questo può essere usato come:

 int f = 0, s = 0; get5and6(&f,&s); // f & s will now be 5 & 6 

O

 void get5and6(int &f, int &s) // using references { f = 5; s = 6; } 

questo può essere usato come:

 int f = 0, s = 0; get5and6(f,s); // f & s will now be 5 & 6 

Quando passiamo per riferimento, passiamo l’indirizzo della variabile. Il passaggio per riferimento è simile al passaggio di un puntatore: in entrambi i casi viene trasmesso solo l’indirizzo.

Ad esempio:

 void SaveGame(GameState& gameState) { gameState.update(); gameState.saveToFile("save.sav"); } GameState gs; SaveGame(gs) 

O

 void SaveGame(GameState* gameState) { gameState->update(); gameState->saveToFile("save.sav"); } GameState gs; SaveGame(&gs); 

Poiché viene passato solo l’indirizzo, non è necessario copiare il valore della variabile (che potrebbe essere davvero enorme per oggetti enormi). Quindi il passaggio per riferimento migliora le prestazioni soprattutto quando:

  1. L’object passato alla funzione è enorme (vorrei usare la variante del puntatore qui in modo che il chiamante sappia che la funzione potrebbe modificare il valore della variabile)
  2. La funzione potrebbe essere chiamata più volte (ad esempio in un ciclo)

Inoltre, leggi i riferimenti const . Quando viene utilizzato, l’argomento non può essere modificato nella funzione.

Questo articolo mi ha aiutato molto.

Si prega di dimenticare i puntatori per ora. E prendi questo con un granello di sale.

Un riferimento è l’object. Quando passi per riferimento, passi l’ object.

Quando passi per valore, passi una copia dell’object; un altro object. Potrebbe avere lo stesso stato , ma è un’istanza diversa ; un clone

Quindi, può essere sensato passare per riferimento se:

  • è necessario modificare l’ object all’interno della funzione
  • non ha bisogno (o vuole) di modificare l’ object, ma vorrebbe evitare di copiarlo solo per passarlo a una funzione. Questo sarebbe un riferimento const .

E può avere senso passare di valore se:

  • voglio iniziare da un gemello identico e lasciare il gemello originale indisturbato
  • non mi interessa il costo di copiare l’object (ad esempio, non vorrei passare un int di riferimento a meno che non volessi modificarlo).

Ecco, dai un’occhiata a questo codice:

 #include struct Foo { Foo() { } void describe() const { std::cout<<"Foo at address "< 

E compilarlo in questo modo: g++ example.cpp

./a.out : ./a.out

E controlla l'output (gli indirizzi reali potrebbero essere diversi nel tuo computer, ma il punto rimarrà):

 Original Foo Foo at address 0x7fff65f77a0f called byreference Foo at address 0x7fff65f77a0f called byvalue Foo at address 0x7fff65f779f0 

Si noti come l'indirizzo called byreference sia lo stesso Original Foo (entrambi sono 0x7fff65f77a0f ). E nota come l'indirizzo called byvalue è diverso0x7fff65f779f0 ).

Portalo su una tacca. Modifica il codice per apparire come segue:

 #include #include // for sleeping struct Foo { Foo() { } Foo(const Foo&) { sleep(10); // assume that building from a copy takes TEN seconds! } void describe() const { std::cout<<"Foo at address "< 

Compilalo allo stesso modo e nota l'output (commenti non nell'output, inclusi per chiarezza):

 Original Foo Foo at address 0x7fff64d64a0e called byreference Foo at address 0x7fff64d64a0e # this point is reached "immediately" called byvalue # this point is reached TEN SECONDS later Foo at address 0x7fff64d64a0f 

Quindi, il codice ha lo scopo di esagerare il costo di una copia: quando hai chiamato per riferimento, questo costo NON è stato sostenuto. Quando hai chiamato per valore, hai dovuto aspettare dieci secondi.

Nota: il mio codice è stato compilato in OS X 10.7.4 utilizzando GCC 4.8.1. Se sei in Windows potresti aver bisogno di qualcosa di diverso da unitsd.h per far funzionare la chiamata a sleep .

Forse questo aiuta.

Vantaggi dell’utilizzo di pass per riferimento: non è necessario creare una copia dei dati che si stanno passando solo il puntatore ad essa in memoria. (una grande vittoria per le prestazioni pensa se hai un object enorme che hai passato in giro). È ansible “restituire” più valori. So che alcune funzioni in c / c ++ restituiscono un numero e uno dei parametri è un puntatore ai dati che vengono manipolati.

Contro l’utilizzo del pass per riferimento: bisogna stare attenti a modificare i dati trasmessi in quanto potrebbero causare effetti collaterali che potreste desiderare o meno.

Per riferimento è ugualmente ansible passare manualmente la variabile tramite puntatore, ma per riferimento non consentirà all’utente di gestire il puntatore “facile da rovinare”.