In C ++, perché non tutti i parametri di funzione dovrebbero essere riferimenti?

7

Attualmente sto imparando C ++ dalla 5 ° edizione di C ++ Primer. Il capitolo sulle funzioni del libro afferma che solo gli oggetti di grandi dimensioni (quelli grandi relativi come le stringhe delle librerie standard contano, ma i "tipi primitivi" come int o char non lo fanno) dovrebbero essere passati come riferimenti.

Ad esempio, la funzione seguente serve come esempio nel libro su quando utilizzare i parametri di riferimento:

string::size_type find_char(const string &s, char c, string::size_type &occurs){
    /* code to return the first occurrence of c in s, occurs contains total number of occurrences */
}

Uno degli esercizi chiede quale sia la ragione per cui ciascun parametro è ciò che è al di là del suo tipo di base. Capisco perché la stringa è un riferimento (per impedire la copia di migliorare le prestazioni) e perché è const (in quanto non ha bisogno di essere modificato). Capisco anche perché il parametro occurs sia un riferimento (poiché il valore della variabile originale deve essere modificato).

Ciò che non capisco è perché c non sia un riferimento (const). So che contiene un oggetto molto piccolo, ma, non impedirebbe la copia di qualsiasi oggetto, indipendentemente da quanto piccolo, sia ancora un miglioramento delle prestazioni?

Se ci fosse qualche svantaggio nell'usare i riferimenti, allora avrebbe senso. Ma, non ho idea di cosa sarebbe un tale svantaggio. Inoltre, so che l'indirizzo di un puntatore verrebbe copiato, ma, poiché i riferimenti non possono essere modificati, immagino che la funzione chiamata possa accedere alla variabile originale esattamente nello stesso modo in cui lo fa il chiamante (assumendo che la variabile sia dichiarata nel chiamante).

Perché dovrebbe mai essere usato un parametro non di riferimento, specialmente in istanze come l'esempio precedente, a meno che il parametro non debba essere modificato per l'implementazione della funzione senza restituire il valore modificato?

NOTA: il corpo della funzione è incluso nel libro, ma, per brevità, viene rimosso da questa domanda.

    
posta john01dav 14.05.2016 - 01:47
fonte

3 risposte

7

Per i tipi di base, i parametri di riferimento solitamente non offrono alcun attributo di prestazione.

Memory

Ad esempio, nel tuo esempio, char richiede un singolo byte. Un tipo di riferimento richiede un puntatore, che di solito è di 4 o 8 byte, in cui la dimensione di un puntatore è direttamente correlata al tipo di eseguibile che si sta utilizzando (4 byte per 32 bit, 8 byte per 64 bit), a causa delle dimensioni del registro di memoria.

Per la maggior parte delle macchine moderne, questo sarà passato in pila, o in un registro, come un singolo valore. Quindi non fa alcuna differenza.

Ora

La copia di un tipo base ( char , int , long ) è in genere una singola istruzione MOV . L'impostazione del puntatore utilizzato in un parametro di riferimento è, di nuovo, di solito una singola istruzione MOV .

    
risposta data 14.05.2016 - 02:31
fonte
6

Ci sono due aspetti negativi nell'utilizzo dei riferimenti:

  1. L'uso dei riferimenti consente ulteriori aliasing. Ciò significa che il compilatore deve ricaricare e memorizzare il valore più spesso a meno che non possa in qualche modo determinare che un valore non è letto / scritto tra due usi nel codice che vede.

  2. Ogni riferimento indiretto implica una lettura aggiuntiva della memoria.

In molti casi, questi due effetti dominano qualsiasi potenziale risparmio dal non dover copiare l'argomento, anche se il tipo è leggermente più grande di un puntatore. Non che ci sia un tale risparmio per tipi banalmente copiabili non più grandi di un puntatore.

Per inciso, dato che C ++ 17 dovresti evitare di passare un const std::string& a favore di un std::string_view :

Quest'ultimo è più flessibile ed efficiente, in quanto non alloca la stringa, riduce l'indirezione e non ha criteri di allocazione.

    
risposta data 24.08.2017 - 12:00
fonte
-1

La mia comprensione è che si desidera "incapsulare" gli argomenti passati a una funzione - la funzione dovrebbe essere in grado di modificare solo ciò che si desidera modificare. Passare sempre un riferimento e metterlo di fronte a esso è noioso e soggetto a errori.

Inoltre, passare sempre riferimenti vuol dire creare una variabile locale per ogni argomento di funzione (invece di passare espressioni).

Inoltre, il passaggio di un riferimento significa che il compilatore dovrà probabilmente generare codice per il dereferenziamento nella funzione chiamata. Per alcuni ABI che passano un riferimento può essere significativamente più lento.

    
risposta data 14.05.2016 - 13:54
fonte

Leggi altre domande sui tag