Domanda

Quale sarebbe la migliore pratica quando si dà a una funzione la variabile originale con cui lavorare:

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

o

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

IOW: c'è qualche motivo per sceglierne uno sopra l'altro?

È stato utile?

Soluzione

La mia regola empirica è:

Utilizzare i puntatori se si desidera eseguire l'aritmetica del puntatore con essi (ad esempio incrementando l'indirizzo del puntatore per passare da un array) o se si deve mai passare un puntatore NULL.

Usa i riferimenti altrimenti.

Altri suggerimenti

Penso davvero che trarrai vantaggio dalla creazione della seguente funzione che chiama le linee guida per la codifica:

  1. Come in tutti gli altri posti, sii sempre const -correct.

    • Nota: questo significa, tra le altre cose, che solo i valori out-out (vedi elemento 3) e i valori passati per valore (vedi elemento 4) possono mancare dell'identificatore const .
  2. Passa un valore tramite puntatore solo se il valore 0 / NULL è un input valido nel contesto corrente.

    • Razionale 1: Come un chiamante , vedi che qualunque cosa passi in deve essere in uno stato utilizzabile.

    • Razionale 2: Come chiamato , sai che tutto ciò che arriva in è in uno stato utilizzabile. Quindi, nessun controllo NULL o gestione degli errori deve essere fatto per quel valore.

    • Razionale 3: Le razionali 1 e 2 saranno applicate dal compilatore . Se riesci, rileva sempre errori al momento della compilazione.

  3. Se un argomento della funzione è un valore out, passalo per riferimento.

    • Motivazione: Non vogliamo violare l'articolo 2 ...
  4. Scegli " passa per valore " oltre "passa per riferimento const" solo se il valore è un POD ( Datastructure vecchio normale ) o abbastanza piccolo ( in termini di memoria) o in altri modi abbastanza economico (in termini di tempo) da copiare.

    • Motivazione: evitare copie non necessarie.
    • Nota: abbastanza piccolo e abbastanza economico non sono misurabili assoluti.

Questo alla fine finisce per essere soggettivo. La discussione finora è utile, ma non credo che ci sia una risposta corretta o decisiva a questo. Molto dipenderà dalle linee guida di stile e dalle tue esigenze al momento.

Mentre ci sono alcune capacità diverse (se qualcosa può essere o meno NULL) con un puntatore, la differenza pratica più grande per un parametro di output è puramente sintassi. Guida allo stile C ++ di Google ( https://google.github.io/styleguide/cppguide.html# Reference_Arguments ), ad esempio, impone solo i puntatori per i parametri di output e consente solo i riferimenti che sono const. Il ragionamento è di leggibilità: qualcosa con sintassi del valore non dovrebbe avere un significato semantico del puntatore. Non sto suggerendo che questo sia necessariamente giusto o sbagliato, ma penso che il punto qui sia che è una questione di stile, non di correttezza.

Dovresti passare un puntatore se stai per modificare il valore della variabile. Anche se tecnicamente passare un riferimento o un puntatore sono gli stessi, passare un puntatore nel tuo caso d'uso è più leggibile in quanto "pubblicizza" il fatto che il valore verrà modificato dalla funzione.

Se si dispone di un parametro in cui potrebbe essere necessario indicare l'assenza di un valore, è prassi comune rendere il parametro un valore puntatore e passare NULL.

Una soluzione migliore nella maggior parte dei casi (dal punto di vista della sicurezza) è utilizzare boost :: opzionale . Ciò consente di passare valori facoltativi come riferimento e anche come valore di ritorno.

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}

Usa un riferimento quando puoi, usa un puntatore quando devi. Da FAQ C ++: " Quando dovrei usare i riferimenti e quando dovrei usare i puntatori? "

Puntatori

  • Un puntatore è una variabile che contiene un indirizzo di memoria.
  • Una dichiarazione del puntatore è composta da un tipo di base, un * e il nome della variabile.
  • Un puntatore può puntare a qualsiasi numero di variabili nella vita
  • A un puntatore che attualmente non punta a una posizione di memoria valida viene assegnato il valore null (che è zero)

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
    
  • L'amplificatore & amp; è un operatore unario che restituisce l'indirizzo di memoria del suo operando.

  • L'operatore di dereferenziazione (*) viene utilizzato per accedere al valore memorizzato nella variabile a cui punta il puntatore.

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;
    

Riferimento

  • Un riferimento (& amp;) è come un alias di una variabile esistente.

  • Un riferimento (& amp;) è come un puntatore costante che viene automaticamente referenziato.

  • Viene solitamente utilizzato per elenchi di argomenti di funzioni e valori di ritorno di funzioni.

  • Un riferimento deve essere inizializzato quando viene creato.

  • Una volta inizializzato un riferimento a un oggetto, non può essere modificato per fare riferimento a un altro oggetto.

  • Non puoi avere riferimenti NULL.

  • Un riferimento const può fare riferimento a un const int. Viene fatto con una variabile temporanea con valore della const

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization
    

 inserisci qui la descrizione dell'immagine

 inserisci qui la descrizione dell'immagine

Un riferimento è un puntatore implicito. Fondamentalmente puoi cambiare il valore a cui punta il riferimento ma non puoi cambiare il riferimento per puntare a qualcos'altro. Quindi i miei 2 centesimi sono che se vuoi solo cambiare il valore di un parametro passalo come riferimento ma se hai bisogno di cambiare il parametro per puntare a un oggetto diverso passalo usando un puntatore.

Considera la parola chiave out di C #. Il compilatore richiede al chiamante di un metodo di applicare la parola chiave out a qualsiasi argomento out, anche se sa già se lo sono. Questo ha lo scopo di migliorare la leggibilità. Anche se con gli IDE moderni sono propenso a pensare che questo sia un lavoro per l'evidenziazione della sintassi (o semantica).

Passa per riferimento const a meno che non ci sia un motivo per cui desideri modificare / conservare i contenuti che stai passando.

Questo sarà il metodo più efficiente nella maggior parte dei casi.

Assicurati di usare const su ogni parametro che non desideri modificare, poiché ciò non solo ti protegge dal fare qualcosa di stupido nella funzione, ma fornisce una buona indicazione agli altri utenti su cosa fa la funzione ai valori passati. Ciò include la creazione di un puntatore const quando si desidera modificare solo ciò che punta a ...

Puntatori:

  • Può essere assegnato nullptr (o NULL ).
  • Nel sito di chiamata, devi usare & amp; se il tuo tipo non è un puntatore stesso, facendo esplicitamente che stai modificando il tuo oggetto.
  • I puntatori possono essere rimbalzati.

References:

  • Non può essere nullo.
  • Una volta associato, non può cambiare.
  • I chiamanti non devono usare esplicitamente & amp; . Questo è considerato a volte male perché devi andare all'implementazione della funzione per vedere se il tuo parametro è stato modificato.

Un riferimento è simile a un puntatore, tranne per il fatto che non è necessario utilizzare un prefisso & # 8727; per accedere al valore a cui fa riferimento il riferimento. Inoltre, non è possibile fare riferimento a un oggetto diverso dopo la sua inizializzazione.

I riferimenti sono particolarmente utili per specificare gli argomenti delle funzioni.

per ulteriori informazioni consultare "Un tour di C ++" di " Bjarne Stroustrup " (2014) Pagine 11-12

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