2009-02-27 13 views
55

C'è una sorta di sottile differenza tra quelli:C++: differenza tra la "commerciale" e "&" e l'asterisco "*" nella dichiarazione di funzione/metodo?

void a1(float &b) { 
    b=1; 
}; 
a1(b); 

e

void a1(float *b) { 
    (*b)=1; 
}; 
a1(&b); 

?

Entrambi fanno lo stesso (o così sembra da main()), ma il primo è ovviamente più breve, tuttavia la maggior parte del codice che vedo usa la seconda notazione. C'è una differenza? Forse nel caso in cui si tratti di qualche oggetto invece di galleggiare?

+1

Ora prova a capire quale vuoto a1 (float && b) significa in C++ 0x. :) –

+0

Sarebbe bello anche aver spiegato perché un LHS e potrebbe essere preferibile a * in alcune situazioni, o addirittura necessario affatto se abbiamo * per i riferimenti. –

+0

@ T.Webster: i riferimenti sono immutabili. Ciò significa che quando ne fai uno si riferisce sicuramente a qualcosa (quindi non può essere NULL, il compilatore ti costringe a specificare in modo esplicito a cosa si riferirà), e una volta che lo hai fatto non può mai essere fatto riferimento a nient'altro. Sono più sicuri perché non puoi accidentalmente fare cose stupide come l'aritmetica dei puntatori quando non intendevi farlo. –

risposta

47

Entrambi fanno lo stesso, ma uno usa i riferimenti e uno usa i puntatori.

See my answer here for a comprehensive list of all the differences.

+1

Com'è questo -1? Upvoted. –

+1

Anche a bassa frequenza, non il mio "-1". Forse qualcuno ha pensato che questo non spiegasse abbastanza. Grazie per la risposta! –

+0

Ottimo post, ho lottato per qualche ora e dopo aver letto il tuo riassunto sono stato benedetto con un "Eureka" -moment;) Grazie @DanielSloof! – polyclick

5

Nel primo esempio con riferimenti, si sa che b non può essere NULL. Con l'esempio del puntatore, b potrebbe essere il puntatore NULL.

Tuttavia, si noti che è possibile passare un oggetto NULL attraverso un riferimento, ma è scomodo e la procedura chiamata può assumere è un errore di averlo fatto:

a1(*(float *)NULL); 
3

Nel secondo esempio il chiamante deve prefisso il nome della variabile con "&" per passare l'indirizzo della variabile.

Questo può essere un vantaggio: il chiamante non può inavvertitamente modificare una variabile passandola come riferimento quando pensavano di passare per valore.

1

Funzionalmente nel vostro esempio, entrambe le versioni fanno lo stesso.

Il primo ha il vantaggio di essere trasparente sul lato chiamata. Immaginate come sarebbe cercare un operatore:

cin >> &x; 

E come appare brutto per un'invocazione di swap

swap(&a, &b); 

Si desidera cambiare a e b. E sembra molto meglio di quando devi prima prendere l'indirizzo. Per inciso, bjarne stroustrup scrive che il motivo principale per i riferimenti era la trasparenza aggiunta al lato chiamata, specialmente per gli operatori. Anche vedere come non è più evidente se le seguenti

&a + 10 

aggiungerebbe 10 al contenuto di un, chiamando l'operatore + di esso, o se si aggiunge 10 a un puntatore temporaneo a un. Aggiungilo all'impossibilità di non sovraccaricare gli operatori solo per gli operandi integrati (come un puntatore e un intero). I riferimenti rendono questo cristallo chiaro.

puntatori sono utili se si vuole essere in grado di mettere un "null":

a1(0); 

Poi nella a1 il metodo può confrontare il puntatore con 0 e vedere se il puntatore punta a qualsiasi oggetto.

+0

(int & a, int & b) non può determinare che la chiamata sia stata effettuata con argomenti identici: swap (x, x); e saltare fuori presto. La versione del puntatore può: if (a == b) return; Non sono sicuro che sia importante, ma è un caso che la versione di riferimento non può gestire. – jmucchiello

+0

joe, può: se (& a == & b) restituire; –

3

A parte lo zucchero sintattico, l'unica vera differenza è la possibilità per un parametro di funzione che è un puntatore di essere nullo. Quindi la versione del puntatore può essere più espressiva se gestisce correttamente il caso nullo. Il caso nullo può anche avere un significato speciale collegato ad esso. La versione di riferimento può operare solo su valori del tipo specificato senza capacità nulla.

21

Sì. La notazione * dice che ciò che viene passato sullo stack è un puntatore, cioè un indirizzo di qualcosa. Il & dice che è un riferimento. L'effetto è simile ma non identico:

Prendiamo due casi:

void examP(int* ip); 
    void examR(int& i); 

    int i; 

Se chiamo examP, scrivo

examP(&i); 

che prende l'indirizzo della voce e lo passa sul pila. Se chiamo examR,

examR(i); 

io non ne ho bisogno; ora il compilatore "in qualche modo" passa un riferimento - che in pratica significa che ottiene e passa l'indirizzo di i. Sul lato codice, quindi

void examP(int* ip){ 
     *ip += 1; 
    } 

Devo fare in modo di dereferenziare il puntatore. ip += 1 fa qualcosa di molto diverso.

void examR(int& i){ 
     i += 1; 
    } 

aggiorna sempre il valore di i.

Per ulteriori informazioni, consultare "chiamata per riferimento" rispetto a "chiamata per valore". La nozione & fornisce una chiamata C++ per riferimento.

+0

Charlie Martin o qualcuno che capisce questa risposta, la prego di riformulare/riscrivere questo per una risposta più concisa? In particolare i due casi di esempio. Credo che qui ci siano informazioni preziose e credo che questa risposta e gli utenti trarrebbero beneficio da una risposta più concisa. Modificherei personalmente la risposta, ma non capisco i due casi esemplificativi. – HappyCoding

+0

Penso che faresti meglio a leggere Stroustrup. Non ti biasimo per essere stato confuso. –

0

Una grande differenza degno di nota è quello che sta succedendo al di fuori, hai i:

a1(something); 

o:

a1(&something); 

mi piace passare gli argomenti per riferimento (sempre uno const :)) quando non vengono modificati nella funzione/metodo (e quindi puoi anche passare oggetti automatici/temporanei all'interno) e passarli per puntatore per significare e avvisare l'utente/lettore del codice che chiama il metodo che l'argomento può e probabilmente è intenzionalmente modificato all'interno.