2009-12-21 51 views
24

Sono un ragazzo C e sto cercando di capire un po 'di codice C++. Ho la seguente dichiarazione di funzione:Cosa fa '&' fare in una dichiarazione C++?

int foo(const string &myname) { 
    cout << "called foo for: " << myname << endl; 
    return 0; 
} 

modo la firma della funzione differiscono dalla C equivalente:

int foo(const char *myname) 

C'è una differenza tra l'utilizzo vs string *mynamestring &myname? Qual è la differenza tra & in C++ e * in C per indicare i puntatori?

Allo stesso modo:

const string &GetMethodName() { ... } 

Qual è il & fa qui? C'è qualche sito web che spiega come lo & sia usato in modo diverso in C vs C++?

+2

Correlato: http://stackoverflow.com/questions/57483/difference-between-pointer-variable-and-reference-variable-in-c –

+2

L'operatore '&' * fa la stessa cosa in C e C++ : prende l'indirizzo di un oggetto. Il prototipo '&' in a function non è un operatore. Correggerò il titolo –

risposta

25

"&" indica un riferimento anziché un puntatore a un oggetto (nel tuo caso un riferimento costante).

Il vantaggio di avere una funzione come

foo(string const& myname) 

sopra

foo(string const* myname) 

è che nel primo caso si ha la garanzia che mionome non è nullo, poiché C++ non consente riferimenti NULL . Poiché si passa per riferimento, l'oggetto non viene copiato, proprio come se si passasse un puntatore.

Il tuo secondo esempio:

const string &GetMethodName() { ... } 

permetterebbe di restituire una costante di riferimento, ad esempio, una variabile membro. Ciò è utile se non si desidera che venga restituita una copia e si garantisca nuovamente che il valore restituito non è nullo. A titolo di esempio, il seguente consente di dirigere, accesso in sola lettura:

class A 
{ 
    public: 
    int bar() const {return someValue;} 
    //Big, expensive to copy class 
} 

class B 
{ 
public: 
A const& getA() { return mA;} 
private: 
A mA; 
} 
void someFunction() 
{ 
B b = B(); 
//Access A, ability to call const functions on A 
//No need to check for null, since reference is guaranteed to be valid. 
int value = b.getA().bar(); 
} 

Bisogna ovviamente fare attenzione a non restituisce i riferimenti non validi. compilatori sarà lieto di compilare il seguente (a seconda del livello di allarme e come si trattano gli avvertimenti)

int const& foo() 
{ 
int a; 

//This is very bad, returning reference to something on the stack. This will 
//crash at runtime. 
return a; 
} 

In sostanza, è vostra responsabilità di garantire che tutto ciò si sta tornando un riferimento alla realtà è valida.

+1

note - il chiamante della funzione foo non sa o non deve scrivere nulla di speciale. Si limita a parlare (mystr); nota quanto è diverso da un foo che prende un puntatore. In tal caso, il chiamante deve andare a pippo (& str). Alcuni sostengono che questo è pericoloso; altri sostengono che è elegante; in entrambi i casi è quello che è – pm100

16

Qui, & non viene utilizzato come operatore. Come parte delle dichiarazioni di funzioni o variabili, & denota un riferimento. Le Domande frequenti su C++ Lite hanno un bel numero di chapter on references.

+0

Ok, reggere: guarda questo sito web (http://cpp-tutorial.cpp4u.com/structures_functions.html). Scorri verso il basso e osserva come tratta la funzione add() usando i riferimenti. Nel corpo della funzione, utilizza "* p" mentre nel tuo collegamento non c'è "*". cosa dà? – poundifdef

+1

Ho visto quel collegamento e usare * in quel contesto è una cattiva idea, o potrebbe anche non funzionare. – Mongoose

+1

Sono abbastanza sicuro che sia un refuso in quel tutorial - probabilmente iniziato come 'int add (int * p)'. – ZoogieZork

6

stringa * e stringa & differiscono in un paio di modi. Prima di tutto, il puntatore punta alla posizione dell'indirizzo dei dati. Il riferimento punta ai dati. Se avete avuto la seguente funzione:

int foo(string *param1); 

Si dovrà verificare nella dichiarazione di funzione per fare in modo che param1 indicò un percorso valido. Comparativamente:

int foo(string &param1); 

Qui, è responsabilità del chiamante per assicurarsi che il indicò dati sono validi. Non è possibile passare un valore "NULL", ad esempio, nella seconda funzione in alto.

Per quanto riguarda la seconda domanda, circa i valori metodo di ritorno essendo un punto di riferimento, considerare i seguenti tre funzioni:

string &foo(); 
string *foo(); 
string foo(); 

Nel primo caso, si sarebbe tornato un riferimento ai dati. Se la dichiarazione di funzione si presentava così:

string &foo() 
{ 
    string localString = "Hello!"; 
    return localString; 
} 

Si sarebbe probabilmente ottenere alcuni errori di compilazione, dal momento che si restituisce un riferimento a una stringa che è stato inizializzato nello stack per quella funzione. Al ritorno della funzione, quell'ubicazione dei dati non è più valida. In genere, si desidera restituire un riferimento a un membro della classe o qualcosa del genere.

La seconda funzione in alto restituisce un puntatore nella memoria effettiva, quindi rimarrebbe lo stesso. Dovresti controllare i puntatori NULL, però.

Infine, nel terzo caso, i dati restituiti verrebbero copiati nel valore di ritorno per il chiamante. Quindi, se la funzione è stata in questo modo:

string foo() 
{ 
    string localString = "Hello!"; 
    return localString; 
} 

saresti a posto, dal momento che la stringa "Ciao" sarebbe stato copiato nel valore di ritorno per tale funzione, accessibili nello spazio di memoria del chiamante.

+0

Dovrebbe anche notare che il secondo caso richiede una stringa allocata nell'heap e si imbatterà negli stessi problemi del primo caso se si utilizza semplicemente l'operatore address-of su una variabile creata dallo stack. –

+1

@sohum Il puntatore punta alla posizione dell'indirizzo dei dati e ai punti di riferimento ai dati. Non sono la stessa cosa, dal momento che entrambi puntano alla posizione dei dati quando vengono usati con Ampersand? –

1

Un modo per osservare l'operatore & (di riferimento) in C++ è semplicemente uno zucchero sintattico per un puntatore. Ad esempio, i seguenti sono grosso modo equivalente:

void foo(int &x) 
{ 
    x = x + 1; 
} 

void foo(int *x) 
{ 
    *x = *x + 1; 
} 

La più utile è quando hai a che fare con una classe, in modo che i metodi di girare da X-> bar() a x.bar().

La ragione per cui ho detto approssimativamente è che l'utilizzo di riferimenti impone ulteriori restrizioni in fase di compilazione su cosa è possibile fare con il riferimento, al fine di proteggersi da alcuni dei problemi causati quando si tratta di puntatori. Ad esempio, non è possibile modificare accidentalmente il puntatore o utilizzare il puntatore in un modo diverso dal riferimento all'oggetto singolare che è stato passato.

0

In questo contesto, & sta causando la funzione per prendere stringname come riferimento. La differenza tra riferimenti e puntatori è:

  • Quando si prende un riferimento a una variabile, che il riferimento è la variabile si fa riferimento. Non è necessario dereferenziarlo o altro, lavorare con il riferimento è semonicamente uguale a lavorare con la variabile di riferimento stessa.
  • NULL non è un valore valido per un riferimento e comporterà un errore del compilatore. In generale, se si desidera utilizzare un parametro di output (o un puntatore/riferimento in generale) in una funzione C++ e si deve consentire il passaggio di un valore nullo a tale parametro, utilizzare un puntatore (o puntatore intelligente, preferibilmente). Se il passaggio di un valore nullo non ha senso per quella funzione, utilizzare un riferimento.
  • Non è possibile "ri-inserire" un riferimento. Mentre il valore di un puntatore può essere modificato per puntare a qualcos'altro, un riferimento non ha funzionalità simili. Una volta che si prende una variabile per riferimento, si sta effettivamente occupando direttamente di quella variabile. Proprio come non è possibile modificare il valore di a scrivendo b = 4;. Il valore di un riferimento è il valore di qualsiasi riferimento.
1
#include<iostream> 
using namespace std; 
int add(int &number); 

int main() 
{ 
      int number; 
      int result; 
      number=5; 
      cout << "The value of the variable number before calling the function : " << number << endl; 
      result=add(&number); 
      cout << "The value of the variable number after the function is returned : " << number << endl; 
      cout << "The value of result : " << result << endl; 
      return(0); 
} 

int add(int &p) 
{ 
      *p=*p+100; 
      return(*p); 
} 

Questo è un codice valido sotto diversi aspetti. L'esecuzione attraverso g ++ dà:

crap.cpp: In function ‘int main()’: 
crap.cpp:11: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int*’ 
crap.cpp:3: error: in passing argument 1 of ‘int add(int&)’ 
crap.cpp: In function ‘int add(int&)’: 
crap.cpp:19: error: invalid type argument of ‘unary *’ 
crap.cpp:19: error: invalid type argument of ‘unary *’ 
crap.cpp:20: error: invalid type argument of ‘unary *’ 

una versione valida del codice legge:

#include<iostream> 
using namespace std; 
int add(int &number); 

int main() 
{ 
      int number; 
      int result; 
      number=5; 
      cout << "The value of the variable number before calling the function : " << number << endl; 
      result=add(number); 
      cout << "The value of the variable number after the function is returned : " << number << endl; 
      cout << "The value of result : " << result << endl; 
      return(0); 
} 

int add(int &p) 
{ 
      p=p+100; 
      return p; 
} 

Quello che sta accadendo qui è che si passa una variabile "così come sono" per la vostra funzione. Questo è più o meno equivalente a:

int add(int *p) 
{ 
     *p=*p+100; 
     return *p; 
} 

Tuttavia, il superamento di un riferimento a una funzione assicura che non si può fare cose come l'aritmetica dei puntatori con il riferimento. Ad esempio:

int add(int &p) 
{ 
      *p=*p+100; 
      return p; 
} 

non valido.

Se necessario utilizzare un puntatore a un riferimento, che deve essere fatto in modo esplicito:

int add(int &p) 
{ 
        int* i = &p; 
      i=i+100L; 
      return *i; 
} 

che su una pista di prova dà (come previsto) Uscita spazzatura:

The value of the variable number before calling the function : 5 
The value of the variable number after the function is returned : 5 
The value of result : 1399090792 
1

La tua funzione dichiara una costante riferimento a una stringa:

int foo(const string &myname) { 
    cout << "called foo for: " << myname << endl; 
    return 0; 
} 

Un riferimento ha alcune proprietà speciali, che lo rendono un'alternativa più sicura ai puntatori in molti modi:

  • non può mai essere NULL
  • deve sempre essere inizializzato
  • esso non può essere cambiato per riferirsi a una variabile diversa, una volta impostato
  • può essere utilizzato esattamente nello stesso modo in cui la variabile a cui fa riferimento (che significa che non è necessario deferenza come un puntatore)

modo la firma funzione differire dal C equivalente:

int foo(const char *myname) 

Ci sono molte differenze, poiché la prima si riferisce direttamente ad un oggetto, mentre const char* occorre deindirizzato per indicare i dati.

C'è una differenza tra l'utilizzo di string * myname vs stringa & myname?

La differenza principale quando si tratta di parametri è che non c'è bisogno di dereference &myname. Un esempio più semplice è:

int add_ptr(int *x, int* y) 
{ 
    return *x + *y; 
} 
int add_ref(int &x, int &y) 
{ 
    return x + y; 
} 

che fanno esattamente la stessa cosa. L'unica differenza in questo caso è che non c'è bisogno di dereference x e y in quanto si riferiscono direttamente alle variabili passate in.

const string &GetMethodName() { ... } 

Qual è il & fa qui? C'è qualche sito web che spiega come & viene utilizzato in modo diverso in C vs C++?

Restituisce un riferimento costante a una stringa. In questo modo il chiamante accede direttamente alla variabile restituita, ma solo in senso di sola lettura. A volte ciò viene utilizzato per restituire membri di dati di stringa senza allocare memoria aggiuntiva.

Ci sono alcune sottigliezze con riferimenti - dare un'occhiata allo C++ FAQ on References per ulteriori dettagli.