2009-02-27 39 views
55

¿Hay algún tipo de diferencia sutil entre los que:C++: ¿diferencia entre ampersand "&" y asterisco "*" en declaración de función/método?

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

y

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

?

Ambos hacen lo mismo (o eso parece desde main()), pero el primero es obviamente más corto, sin embargo, la mayor parte del código que veo usa segunda notación. ¿Hay una diferencia? Tal vez en caso de que sea un objeto en lugar de flotar?

+1

Ahora trata de averiguar qué significa void a1 (float && b) en C++ 0x. :) –

+0

Sería bueno tener también explicado por qué un LHS y podría ser preferible a * en algunas situaciones, o incluso necesario si tenemos * para referencias. –

+0

@ T.Webster: las referencias son inmutables. Eso significa que cuando haces uno, definitivamente se refiere a algo (por lo que no puede ser NULO, el compilador te obliga a especificar explícitamente a qué se referirá), y una vez que lo hayas hecho, nunca se puede hacer que se refiera a algo más. Están más seguros porque no puedes hacer cosas tontas como la aritmética del puntero cuando no tenías la intención de hacerlo. –

Respuesta

47

Ambos hacen lo mismo, pero uno usa referencias y uno usa punteros.

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

+1

¿Cómo es esto -1? Upvoted. –

+1

Voto elevado también, no mi "-1". Quizás alguien sintió que esto no explicaba lo suficiente. ¡Gracias por la respuesta! –

+0

Gran publicación, he estado luchando por unas pocas horas y después de leer su resumen fui bendecido con un "Eureka" -moment;) Gracias @DanielSloof! – polyclick

5

En el primer ejemplo con referencias, usted sabe que b no puede ser NULO. Con el ejemplo del puntero, b podría ser el puntero NULL.

Sin embargo, tenga en cuenta que es posible pasar un objeto nulo a través de una referencia, pero es incómoda y el procedimiento de llamada puede asumir que es un error que lo han hecho:

a1(*(float *)NULL); 
3

En el segundo ejemplo el llamador tiene que ponerle un prefijo al nombre de la variable con '&' para pasar la dirección de la variable.

Esto puede ser una ventaja: la persona que llama no puede modificar inadvertidamente una variable pasándola como referencia cuando pensaban que estaban pasando por valor.

1

Funcionalmente en su ejemplo, ambas versiones hacen lo mismo.

La primera tiene la ventaja de ser transparente por el lado de la llamada. Imaginar cómo se vería por un operador:

cin >> &x; 

Y cómo se ve feo para una invocación de intercambio

swap(&a, &b); 

usted quiere intercambiar a y b. Y se ve mucho mejor que cuando primero tiene que tomar la dirección. Por cierto, bjarne stroustrup escribe que la razón principal de las referencias fue la transparencia que se agregó en el lado de la llamada, especialmente para los operadores. También ver cómo no es obvio más si la siguiente

&a + 10 

añadiría a 10 el contenido de una, llamando al operador + de la misma, o si se añade 10 a un puntero temporal a una. Agregue eso a la imposibilidad de que no pueda sobrecargar operadores solo para operandos incorporados (como un puntero y un entero). Las referencias lo hacen claro como el cristal.

punteros son útiles si usted quiere ser capaz de poner un "nulo":

a1(0); 

Luego, en el método A1 se puede comparar con el puntero 0 y ver si el puntero apunta a cualquier objeto.

+0

swap (int & a, int & b) no puede determinar que la llamada se realizó con argumentos idénticos: swap (x, x); y salta temprano. La versión del puntero puede: if (a == b) return; No estoy seguro de que sea importante, pero es un caso que la versión de referencia no puede manejar. – jmucchiello

+0

joe, puede: if (& a == & b) return; –

3

Aparte del azúcar sintáctico, la única diferencia real es la capacidad de un parámetro de función que es un puntero a ser nulo. Entonces, la versión del puntero puede ser más expresiva si maneja el caso nulo correctamente. El caso nulo también puede tener algún significado especial asociado. La versión de referencia solo puede operar en valores del tipo especificado sin una capacidad nula.

21

Sí. La notación * dice que lo que se está pasando en la pila es un puntero, es decir, la dirección de algo. El & dice que es una referencia. El efecto es similar, pero no idéntico:

Tomemos dos casos:

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

    int i; 

Si llamo examP, escribo

examP(&i); 

que toma la dirección del artículo y se lo pasa en el apilar. Si llamo examR,

examR(i); 

Yo no lo necesito; ahora el compilador "de alguna manera" pasa una referencia, lo que prácticamente significa que obtiene y pasa la dirección de i. En el lado del código, entonces

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

Tengo que asegurarme de eliminar la referencia del puntero. ip += 1 hace algo muy diferente.

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

siempre actualiza el valor de i.

Para tener más en cuenta, lea "llamar por referencia" en lugar de "llamar por valor". La noción & proporciona una llamada C++ por referencia.

+0

Charlie Martin o alguien que entiende esta respuesta, ¿podría volver a redactar/reescribir esta pregunta para obtener una respuesta más concisa? Específicamente los dos casos de ejemplo. Creo que hay información valiosa aquí y creo que esta respuesta y los usuarios se beneficiarían de una respuesta más concisa. Yo mismo edité la respuesta, pero no entiendo los dos ejemplos de casos. – HappyCoding

+0

Creo que harías mejor en leer Stroustrup. No te culpo por estar confundido. –

0

Una gran diferencia a destacar es lo que está pasando en el exterior, o bien tienen:

a1(something); 

o:

a1(&something); 

me gusta pasar argumentos por referencia (siempre una const :)) cuando no están modificados en la función/método (y luego también puede pasar objetos automáticos/temporales dentro) y pasarlos por un puntero para indicar y alertar al usuario/lector del código llamando al método que el argumento puede y probablemente sea intencionalmente modificado dentro

Cuestiones relacionadas