2009-12-21 16 views
24

Soy un tipo C y estoy tratando de entender algunos códigos C++. Tengo la siguiente declaración de la función:¿Qué hace '&' en una declaración de C++?

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

¿Cómo funciona la firma de la función difieren del equivalente de C:

int foo(const char *myname) 

¿Hay una diferencia entre el uso string *myname vs string &myname? ¿Cuál es la diferencia entre & en C++ y * en C para indicar punteros?

mismo modo:

const string &GetMethodName() { ... } 

¿Qué está haciendo aquí el &? ¿Hay algún sitio web que explique cómo se usa & de manera diferente en C vs C++?

+2

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

+2

El operador '&' * hace lo mismo en C y C++ : toma la dirección de un objeto. El '&' en un prototipo de función no es un operador. Arreglaré el título. –

Respuesta

25

El "&" denota una referencia en lugar de un puntero a un objeto (en su caso, una referencia constante).

La ventaja de tener una función como

foo(string const& myname) 

sobre

foo(string const* myname) 

es que en el primer caso se le garantiza que myname es no nulo, ya que C++ no permite referencias nulas . Como está pasando por referencia, el objeto no se copia, como si estuviera pasando un puntero.

Su segundo ejemplo:

const string &GetMethodName() { ... } 

le permitiría devolver una referencia constante para, por ejemplo, una variable miembro. Esto es útil si no desea que se devuelva una copia, y nuevamente se le garantiza que el valor devuelto no es nulo. Como ejemplo, lo siguiente le permite acceso directo, de solo lectura:

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(); 
} 

Por supuesto, tenga cuidado de no devolver las referencias no válidas. compiladores se compilan felizmente lo siguiente (dependiendo de su nivel de advertencia y cómo tratar a las advertencias)

int const& foo() 
{ 
int a; 

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

Básicamente, es su responsabilidad asegurarse de que todo lo que va a devolver una referencia a la realidad es válido.

+1

nota: la persona que llama de la función foo no sabe o tiene que escribir algo especial. Él solo va foo (mystr); Observe cuán diferente es esto de un foo que toma un puntero. En ese caso, la persona que llama debe ir a foo (& str). Algunos argumentarían que esto es peligroso; otros argumentan que es elegante; de cualquier manera es lo que es – pm100

16

Aquí, & no se utiliza como operador. Como parte de las declaraciones de funciones o variables, & denota una referencia. El C++ FAQ Lite tiene un muy ingenioso chapter on references.

+0

Bien, espera: mira este sitio web (http://cpp-tutorial.cpp4u.com/structures_functions.html). Desplácese hasta la parte inferior y vea cómo trata la función add() usando referencias. En el cuerpo de la función, usa "* p" mientras que en su enlace, no hay "*". ¿lo que da? – poundifdef

+1

Miré ese enlace, y usar * en ese contexto es una mala idea, o incluso podría no funcionar. – Mongoose

+1

Estoy bastante seguro de que es un error tipográfico en ese tutorial, probablemente comenzó como 'int add (int * p)'. – ZoogieZork

6

cadena * y cadena & difieren en un par de formas. En primer lugar, el puntero apunta a la ubicación de la dirección de los datos. La referencia apunta a los datos. Si usted tuvo la siguiente función:

int foo(string *param1); 

Usted tendría que comprobar en la declaración de función para asegurarse de que param1 señaló una ubicación válida. Comparativamente:

int foo(string &param1); 

Aquí, es la responsabilidad del que llama asegurarse de que los datos apuntados son válidos. No puede pasar un valor "NULO", por ejemplo, int la segunda función anterior.

En cuanto a su segunda pregunta, sobre los valores de retorno método siendo una referencia, tenga en cuenta las siguientes tres funciones:

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

En el primer caso, se estaría devolviendo una referencia a los datos. Si su declaración de la función era la siguiente:

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

Es probable que obtener algunos errores de compilación, ya que va a devolver una referencia a una cadena que fue inicializado en la pila para esa función. En el retorno de la función, esa ubicación de datos ya no es válida. Por lo general, le gustaría devolver una referencia a un miembro de la clase o algo así.

La segunda función anterior devuelve un puntero en la memoria real, por lo que se mantendría igual. Sin embargo, debería verificar los punteros NULL.

Finalmente, en el tercer caso, los datos devueltos se copiarán en el valor de retorno de la persona que llama. Así que si su función era la siguiente:

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

Usted estaría bien, ya que la cadena "Hola" se copia en el valor de retorno para esa función, accesible en el espacio de memoria de la persona que llama.

+0

También debe tener en cuenta que el segundo caso requiere una cadena asignada en el montón y se encontrará con los mismos problemas que en el primer caso si simplemente usa el operador de dirección en una variable creada por la pila. –

+1

@sohum El puntero apunta a la ubicación de dirección de los datos y los puntos de referencia a los datos. ¿No son estas las mismas cosas, ya que ambas apuntan a la ubicación de los datos cuando se usan con Ampersand? –

1

Una forma de ver el operador & (referencia) en C++ es que no es más que un azúcar sintáctico para un puntero. Por ejemplo, los siguientes son más o menos equivalente:

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

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

El más útil es cuando se está tratando con una clase, para que sus métodos se vuelven de x-> barra() para x.bar().

La razón por la que dijo más o menos es que el uso de referencias impone restricciones de tiempo de compilación adicionales sobre lo que puede hacer con la referencia, con el fin de protegerse de algunos de los problemas causados ​​cuando se trata de punteros. Por ejemplo, no puede cambiar accidentalmente el puntero ni usar el puntero de ninguna otra forma que no sea para hacer referencia al objeto singular que le han pasado.

0

En este contexto & hace que la función tome stringname por referencia. La diferencia entre las referencias y punteros es:

  • Cuando se toma una referencia a una variable, que la referencia es la variable que hace referencia. No necesita desreferenciarlo ni nada, trabajar con la referencia es semestralmente igual que trabajar con la variable a la que se hace referencia.
  • NULL no es un valor válido para una referencia y dará como resultado un error de compilación. Por lo tanto, generalmente, si desea utilizar un parámetro de salida (o un puntero/referencia en general) en una función de C++, y se debe permitir pasar un valor nulo a ese parámetro, utilice un puntero (o puntero inteligente, preferiblemente). Si pasar un valor nulo no tiene sentido para esa función, use una referencia.
  • No puede 'volver a sentar' una referencia. Si bien el valor de un puntero se puede cambiar para apuntar a otra cosa, una referencia no tiene una funcionalidad similar. Una vez que toma una variable por referencia, efectivamente está tratando con esa variable directamente. Al igual que no puede cambiar el valor de a escribiendo b = 4;. El valor de una referencia es el valor de lo que sea que haga referencia.
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); 
} 

Esto es código no válido en varios aspectos. Ejecución a través de g ++ da:

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 versión válida del código lee:

#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; 
} 

Lo que ocurre aquí es que usted está pasando una variable "tal cual" a su función. Esto es más o menos equivalente a:

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

Sin embargo, pasando una referencia a una función asegura que no se puede hacer cosas como la aritmética de punteros con la referencia. Por ejemplo:

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

no es válido.

Si debe utilizar un puntero a una referencia, que tiene que ser hecho de manera explícita:

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

Qué en una prueba da (como se esperaba) Salida de basura:

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

Su función declara una referencia constante en una cadena:

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

Una referencia tiene algunas propiedades especiales, que lo convierten en una alternativa más segura a los punteros de muchas maneras:

  • nunca puede ser nulo
  • siempre debe ser inicializado
  • que no se puede cambiar para referirse para establecer una variable diferente una vez
  • se puede utilizar exactamente de la misma manera que la variable a la que se refiere (lo que significa que no es necesario que la deferencia como un puntero)

¿Cómo la firma de la función difieren de la C equivalente:

int foo(const char *myname) 

hay varias diferencias, ya que el primero se refiere directamente a un objeto, mientras que const char* debe eliminan las referencias para apuntar a los datos.

¿Hay alguna diferencia entre el uso de la cadena * myname vs string & myname?

La principal diferencia cuando se trata de parámetros es que no necesita desreferencia &myname. Un ejemplo más simple es:

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

que hacen exactamente lo mismo. La única diferencia en este caso es que no es necesario eliminar la referencia x y y ya que se refieren directamente a las variables pasadas en.

const string &GetMethodName() { ... } 

¿Cuál es el & haciendo aquí? ¿Hay algún sitio web que explique cómo se usa & de manera diferente en C vs C++?

Esto devuelve una referencia constante a una cadena. De modo que la persona que llama puede acceder directamente a la variable devuelta, pero solo en un sentido de solo lectura. Esto a veces se usa para devolver miembros de datos de cadena sin asignar memoria extra.

Hay algunas sutilezas con referencias; eche un vistazo a C++ FAQ on References para obtener más información.

Cuestiones relacionadas