Domanda

Quali sono i vantaggi del passaggio per puntatore sul passaggio per riferimento in C ++?

Ultimamente, ho visto un numero di esempi che hanno scelto di passare argomenti di funzione tramite puntatori anziché passare per riferimento. Ci sono dei vantaggi nel fare questo?

Esempio:

func(SPRITE *x);

con una chiamata di

func(&mySprite);

vs.

func(SPRITE &x);

con una chiamata di

func(mySprite);
È stato utile?

Soluzione

Un puntatore può ricevere un parametro NULL, un parametro di riferimento no. Se c'è mai la possibilità che tu voglia passare "nessun oggetto", usa un puntatore invece di un riferimento.

Inoltre, il passaggio tramite puntatore consente di vedere esplicitamente nel sito della chiamata se l'oggetto viene passato per valore o per riferimento:

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);

Altri suggerimenti

Passando per puntatore

  • Il chiamante deve prendere l'indirizzo - > non trasparente
  • È possibile fornire un valore 0 per indicare nothing . Questo può essere usato per fornire argomenti opzionali.

Passa per riferimento

  • Il chiamante passa appena l'oggetto - > trasparente. Deve essere utilizzato per il sovraccarico dell'operatore, poiché il sovraccarico per i tipi di puntatore non è possibile (i puntatori sono tipi incorporati). Quindi non puoi fare string s = & amp; str1 + & amp; str2; usando i puntatori.
  • Nessun valore possibile 0 - > La funzione chiamata non deve controllarli
  • Il riferimento a const accetta anche i provvisori: void f (const T & amp; t); ... f (T (a, b, c)); , i puntatori non possono essere usati in questo modo poiché non puoi prendere l'indirizzo di un temporaneo.
  • Ultimo ma non meno importante, i riferimenti sono più facili da usare - > meno possibilità di bug.

Allen Holub "abbastanza corda per spararti nel piede" elenca le seguenti 2 regole:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

Elenca diverse ragioni per cui i riferimenti sono stati aggiunti al C ++:

  • sono necessari per definire i costruttori di copie
  • sono necessari per sovraccarichi dell'operatore
  • I riferimenti
  • const ti consentono di avere una semantica pass-by-value evitando una copia

Il suo punto principale è che i riferimenti non dovrebbero essere usati come parametri 'output' perché nel sito di chiamata non c'è alcuna indicazione se il parametro sia un riferimento o un parametro valore. Quindi la sua regola è quella di usare solo argomenti const come argomenti.

Personalmente, penso che questa sia una buona regola empirica in quanto lo rende più chiaro quando un parametro è un parametro di output o meno. Tuttavia, sebbene io sia personalmente d'accordo con questo in generale, mi permetto di essere influenzato dalle opinioni degli altri nel mio team se sostengono parametri di output come riferimenti (ad alcuni sviluppatori piace immensamente).

Mi piace il ragionamento di un articolo tratto da " cplusplus.com: "

  
      
  1. Passa per valore quando la funzione non vuole modificare il parametro e il valore è facile da copiare (ints, doubles, char, bool, ecc ... tipi semplici. std :: string, std :: vector e tutti gli altri contenitori STL NON sono tipi semplici.)

  2.   
  3. Passa dal puntatore const quando il valore è costoso da copiare E la funzione non vuole modificare il valore puntato a AND NULL è un valore atteso e valido che la funzione gestisce.

  4.   
  5. Passa dal puntatore non const quando il valore è costoso da copiare E la funzione vuole modificare il valore puntato a AND NULL è un valore atteso e valido che la funzione gestisce.

  6.   
  7. Passa per riferimento const quando il valore è costoso da copiare E la funzione non vuole modificare il valore a cui fa riferimento AND NULL non sarebbe un valore valido se invece fosse usato un puntatore.

  8.   
  9. Passa per riferimento non cont quando il valore è costoso da copiare E la funzione vuole modificare il valore a cui fa riferimento AND NULL non sarebbe un valore valido se invece fosse usato un puntatore.

  10.   
  11. Quando si scrivono le funzioni del modello, non esiste una risposta chiara perché ci sono alcuni compromessi da considerare che esulano dallo scopo di questa discussione, ma è sufficiente dire che la maggior parte delle funzioni del modello accetta i loro parametri valore o (const) riferimento, tuttavia, poiché la sintassi dell'iteratore è simile a quella dei puntatori (asterisco a "quotazione"), qualsiasi funzione modello che prevede iteratori come argomenti accetterà anche di default anche i puntatori (e non controlla NULL poiché il Il concetto di iteratore NULL ha una sintassi diversa).

  12.   
     

http://www.cplusplus.com/articles/z6vU7k9E/

Quello che prendo da questo è che la differenza principale tra la scelta di usare un puntatore o un parametro di riferimento è se NULL è un valore accettabile. Questo è tutto.

Se il valore è input, output, modificabile ecc. dovrebbe essere nella documentazione / commenti sulla funzione, dopo tutto.

Chiarimenti ai post precedenti:


I riferimenti sono NON una garanzia per ottenere un puntatore non nullo. (Anche se spesso li trattiamo come tali.)

Mentre un codice orribilmente negativo, come nel portarti dietro il codice cattivo , il seguente sarà compilare & amp; run: (Almeno sotto il mio compilatore.)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

Il vero problema che ho con i riferimenti si trova con altri programmatori, d'ora in poi definiti IDIOTS , che allocano nel costruttore, deallocano nel distruttore, e non è possibile fornire un costruttore di copie o un operatore = ().

Improvvisamente c'è un mondo di differenza tra foo (BAR bar) e foo (BAR & amp; bar) . (Viene invocata l'operazione di copia bit a bit automatica. La deallocazione in distruttore viene invocata due volte.)

Per fortuna i compilatori moderni prenderanno questa doppia deallocazione dello stesso puntatore. 15 anni fa, non lo fecero. (Sotto gcc / g ++, usa setenv MALLOC_CHECK_ 0 per rivisitare i vecchi metodi.) Risulta, sotto DEC UNIX, nella stessa memoria allocata a due oggetti diversi. Molto divertente il debug lì ...


Più praticamente:

  • I riferimenti nascondono che stai modificando i dati archiviati da qualche altra parte.
  • È facile confondere un riferimento con un oggetto copiato.
  • I puntatori lo rendono ovvio!

Non proprio. Internamente, il passaggio per riferimento viene eseguito essenzialmente passando l'indirizzo dell'oggetto referenziato. Quindi, non c'è davvero alcun guadagno di efficienza da ottenere passando un puntatore.

Il passaggio per riferimento ha tuttavia un vantaggio. Hai la garanzia di avere un'istanza di qualunque oggetto / tipo venga passato. Se passi un puntatore, corri il rischio di ricevere un puntatore NULL. Usando il pass-by-reference, stai spingendo un controllo NULL implicito di un livello al chiamante della tua funzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top