2009-03-21 18 views
29

Digamos que tiene una función que modifica una variable.Funciones de C++: signo & vs asterisco

¿Debería escribirlo así: void myfunc(int *a) o void myfunc(int &a)?

Las antiguas fuerzas que llame a la función con myfunc(&b) para que quien llama es consciente de que b serán modificados, pero el último es más corto y puede ser llamado simplemente con myfunc(b). Entonces, ¿cuál es mejor usar? ¿Hay algo más que me estoy perdiendo?

+0

Hay una gran cantidad de artículos y temas acerca de este tema http://www.google.ru/search?q=pointer+vs+ referencia – abatishchev

Respuesta

36

Los punteros (es decir, el '*') se deben usar cuando el paso "NULL" sea significativo. Por ejemplo, puede usar un NULO para representar que un objeto en particular necesita ser creado, o que una acción en particular no necesita ser tomada. O si alguna vez necesita ser llamado desde un código que no sea C++. (por ejemplo, para uso en bibliotecas compartidas)

por ejemplo. La función libc time_t time (time_t *result);

Si result no es NULO, se almacenará la hora actual. Pero si result es NULL, entonces no se realiza ninguna acción.

Si la función que está escribiendo no necesita usar NULL como valor significativo, entonces usar referencias (es decir, '&') probablemente sea menos confuso, suponiendo que es la convención que usa su proyecto.

+1

Ah, buen punto. Ahora no me siento tan mal sobre el uso de una referencia en su lugar. Mucho más fácil para escribir la función con una referencia que un puntero. Gracias :) – mpen

12

Siempre que sea posible, uso referencias sobre punteros. La razón de esto es que es mucho más difícil arruinar una referencia que un puntero. Las personas siempre pueden pasar NULL a un valor de puntero, pero no hay tal equivalente a una referencia.

El único inconveniente real es que los parámetros de referencia en C++ carecen de documentación del sitio de llamadas. Algunas personas creen que eso hace que sea más difícil entender el código (y estoy de acuerdo en cierta medida). Por lo general se especifique lo siguiente en mi código y lo uso para la documentación del sitio llamada falsa

#define byref 
... 
someFunc(byref x); 

Por supuesto, esto no hace cumplir la documentación del sitio llamada. Simplemente proporciona una manera muy pobre de documentarlo. Hice algo de experimentación con una plantilla que impone la documentación del sitio de llamada. Sin embargo, esto es más por diversión que por código de producción real.

http://blogs.msdn.com/jaredpar/archive/2008/04/03/reference-values-in-c.aspx

+0

Estoy de acuerdo con que el sitio de llamada no se auto-documenta. Sin embargo, tenga en cuenta que las funciones que modifican los parámetros de referencia casi siempre deben tener un nombre apropiado. Por ejemplo, Point.GetCoords (int & x, int y &y);, mypoint.GetCoords (x, y); – strager

1

Las antiguas fuerzas que llame a la función con myfunc (& b) por lo que la persona que llama es consciente de que B se modificará

veces función podría aceptar puntero constante y la persona que llama tendrá el pensamiento erróneo de que b será modificado.

Mi recomendación: prefiero usar referencias donde sea posible (por supuesto, donde sea necesario). En caso de argumento de función, obtenemos beneficios:
- las referencias no pueden ser NULL - nos ayudan a evitar errores y comprobaciones o comprobaciones innecesarias.
- las referencias tienen solo un punto de inicialización y en la función boody siempre se sabe en qué cosa apunta el parámetro de entrada.

Soy mantenedor en proyecto grande. Y en cualquier caso, estoy buscando la definición de la función antes de invocarla. Por supuesto, cuando busco la definición de función veo la definición de argumentos por valor, por referencia, por referencia constante o por puntero.

Pero parece una cuestión de guerra santa, los pueblos diferentes tienen una visión diferente sobre este punto. P.ej. Google Codding convension uso recomendado punteros en argumentos que podría ser cambiado y permitieron referencias solamente const: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments

todos los parámetros pasados ​​por referencia deben ser etiquetados const.

7

Creo que no estaría de acuerdo con @bb y @JaredPar y me inclino hacia el lado opuesto de la guía. Después de años de intentar apoyar el código C++ de otras personas, a menudo encuentro problemas al acecho en los efectos secundarios no obvios de los argumentos de referencia.Con C#, es obvio ya que tiene que prefijar tipos de argumentos con 'ref'/'out', pero las referencias son potencialmente confusas en C++. Entonces, me gustan los indicadores porque es muy claro que algo está volviendo. Si no te gustan los puntos, C++ no es para ti.

+4

estoy de acuerdo contigo Un problema serio con las referencias es que 'F1 (int), f2 (int &)', se llaman. del mismo modo 'F1 (a), f2 (a)' pero hay una gran diferencia. Y eso puede ser realmente el dolor para depurar dicho código. Y es un error fácil. – Ismael

0

En un mundo ideal, se trata de si el parámetro es o no una salida opcional. ¿Quieres permitir que la persona que llama pase NULL si no les importa? Luego usa un puntero. De lo contrario, una referencia es una mejor opción.

Tenga en cuenta que, en ambos casos, el lenguaje hace que sea engorroso documentar un parámetro de salida. Es mucho mejor si toda la base de código es const correcta, entonces los usuarios pueden suponer que cualquier referencia no const o parámetro de puntero es una salida.

4

Baje por el lado del puntero de la cerca, por las razones citadas aquí y en todas partes. Sin embargo, le diré que sea lo que sea que decida, debe ser coherente y documentarlo en su guía de estilo.

Google C++ guía de estilo bans argumentos de referencia no constantes.

+0

El enlace proporcionado no está actualizado. ¿Alguien por favor actualizarlo? – Jason

+0

Además, se puede considerar que la guía de estilo de Google puede no ser la mejor referencia para las buenas prácticas: https://www.linkedin.com/pulse/20140503193653-3046051-why-google-style-guide-for-c-is -a-deal-breaker – Jason

+0

Depende del proyecto. –

0

Una diferencia, como se ha mencionado anteriormente, es que no puede pasar una referencia nula, pero puede pasar un puntero nulo.

Otra cosa, también ya se ha mencionado, cuando se llama f(a,b) podría haber confusión si la persona que llama no sabe que f podría potencialmente cambiar el valor de b

Sin embargo, otro problema, que es bastante sutil, pero I still ran into it, es la semántica de las referencias.

Los indicadores se transmiten por su valor, pero las referencias no lo son.

Lo que significa que, si pasa un parámetro por un puntero, puede cambiar el puntero y hacer que apunte a otra cosa.

Considera:

void f1_ptr(type * a) 
{ 
    a = new type(); //no change to passed parameters, you're changing the pointer which was passed by value 
} 

void f2_ptr(type * a) 
{ 
    *a = some_other_value; //now you're changing the value of the parameter that was passed 

    //or, if type is a class or struct: 

    a->some_method_that_modifies_object(); //again, changing the parameter that was passed 
} 

Pero, al pasar por referencia, no se puede cambiar la referencia para referirse a otro valor. Una vez que se establece la referencia, no se puede cambiar.

void f3_ref(type& a) 
{ 
    a = type(); //the referred variable has also changed 
} 

//.... 

type obj = type(params); 

f3_ref(obj); //obj now changed 

f1_ptr(&obj); //obj doesn't change 

f2_ptr(&obj); //obj now changed 
+0

En f2_ptr, a los cambios para el alcance de f2_ptr. En otras palabras, obj no se modifica cuando se llama a f2_ptr (suponiendo que el valor original de a (obj) no se usa en f2_ptr). – strager

1

Me gusta pasar por referencia si NULL no tiene importancia, pero puedo ver los argumentos para ambos.Si usted tiene cuidado de ti codificación probablemente podría eliminar la accidentales objeción pase por referencia, asegurándose de que siempre pasa sus variables por referencia constante, por ejemplo:

myfunc(const_cast< const int& >(a)); 

// Alternatively, this approach may require additional handling 
// in the function, but it's cleaner at call point 
myfunc(boost::cref(a)); 

Esa es una gran cantidad de código extra para poco beneficio, aunque. Como señaló Kenny, C# trató esto desde el extremo opuesto (requiriendo paso específico por referencia), pero esa no es una opción para C++ (a menos que, por ejemplo, escribas tus funciones para tomar un contenedor de referencia como su parámetro, como boost :: ref (param)), por ejemplo:

void myfunc(const boost::reference_wrapper<int>& a) { ... } 

Arreglar el problema de puntero es más problemático, aunque ... no hay forma de tiempo de compilación para asegurar el puntero es válido, por lo que terminan con cualquiera de los problemas de funcionamiento de los problemas de puntero, o verificaciones de tiempo de ejecución, o ambos. Esta es la naturaleza de los indicadores.

De todos modos, eso es sólo mi opinión, por lo que vale la pena.

+0

Derecha. Pero, ¿por qué tomarías la referencia_caperita por referencia, y no por valor? –

+0

Como es una clase, y pasarla por referencia es probablemente más rápido que copiarla (aunque se puede copiar). Ambos funcionarán; de esta manera es probablemente menos sobrecarga. – Nick

+0

bien, reference_wrapper pesa exactamente tanto como un puntero. Pensé que esto era obvio. –

1

Algo a tener en cuenta, si está usando funtores stl, es más fácil si el parámetro coincide con el tipo de valor contenedor.

void foo(Bar *); 

void frobnicate(vector<Bar *> vecBars) 
{ 
    for_each(vecBars.begin(), 
      vecBars.end(), 
      ptr_fun(&foo)); 
} 

El código anterior es mucho más difícil si se toma foo bar &