2009-08-24 7 views
7

Siempre que desee pasar un parámetro modificable a una función, ¿qué debo elegir: pasarlo por un puntero o pasarlo por referencia?Pasar un parámetro modificable a la función C++

  1. bool GetFoo (Foo & whereToPlaceResult);
  2. bool GetFoo (Foo * whereToPlaceResult);

Pregunto esto porque siempre consideré la mejor práctica pasar parámetros por referencia (1), pero después de examinar alguna base de datos de códigos locales, llegué a la conclusión de que la forma más común es (2) . Además, el hombre mismo (Bjarne Stroustrup) recomienda usar (2). ¿Cuáles son las [des] ventajas de (1) y (2), o es solo una cuestión de gusto personal?

+1

¿Tiene una referencia para Stroustrup que recomienda 2? Me parece recordar lo contrario. – luke

+3

Como dice Stroustrup, GetFoo (& Data) es más obvio que GetFoo (Data), si la función debe cambiar el valor de los datos. Encontraré la cita exacta más tarde hoy ... – SadSido

+0

Stroustrup lo recomienda en el libro TC++ PL. –

Respuesta

21

prefiero una referencia en lugar de un puntero cuando:

  • No puede ser nulo
  • No se puede cambiar (a apuntar a otra cosa)
  • No debe ser borrado (por quienquiera que reciba el puntero)

Algunas personas dicen sin embargo que la diferencia entre una referencia y una referencia constante es demasiado sutil para muchas personas, y es invisible en el código que llama al método (es decir, si usted lee el código de llamada que pasa un parámetro por referencia, no puede ver si es una referencia const o non-const), y que por lo tanto debe convertirlo en un puntero (para hacerlo explícito en el código de llamada que ' re dando la dirección de su variable, y que por lo tanto el valor de su variable puede ser alterado por el destinatario).

yo personalmente prefiero una referencia, por la siguiente razón:

  1. creo que una rutina debe saber qué subrutina que está llamando
  2. Una subrutina no debe asumir nada acerca de lo que la rutina que está siendo llamado desde.

[1.] implica que hacer que la mutabilidad sea visible para la persona que llama no importa mucho, porque la persona que llama ya debe (por otros medios) comprender lo que hace la subrutina (incluido el hecho de que modificará el parámetro)

[2.] implica que, si se trata de un puntero, la subrutina debe considerar la posibilidad de que el parámetro sea un puntero nulo, que puede ser un código extra e IMO inútil.

Además, cada vez que veo un puntero, pienso, "¿quién va a eliminar esto y cuándo?", Así que siempre/cuando la propiedad/duración/eliminación no sea un problema, prefiero usar una referencia.

Por lo que vale, tengo la costumbre de escribir código const-correct: así que si declaro que un método tiene un parámetro de referencia no constante, el hecho de que no const es significativo. Si las personas no estuvieran escribiendo código const correcto, tal vez sería más difícil saber si un parámetro se modificará en una subrutina, y el argumento para otro mecanismo (por ejemplo, un puntero en lugar de una referencia) sería un poco más fuerte.

+3

Los punteros pasados ​​a un método no se pueden cambiar para apuntar a otro lado. –

+0

@Lou - Eso es verdad; o al menos, * puede * modificarse, pero ese cambio no afectaría a la persona que llama (es decir, el destinatario cambiaría el valor de la copia local del puntero del destinatario). – ChrisW

+0

@Lou, la copia local del puntero puede, y algunas veces lo hace, cambiarse. Con frecuencia, los principiantes tropiezan y piensan que el puntero de la persona que llama ahora también ha cambiado. Otra razón para mantenerse alejado del n. ° 2, (o al menos evitar modificar los argumentos del puntero) en mi humilde opinión. –

1

Elijo # 2 porque es obvio en el momento de la llamada que se cambiará el parámetro.

getFoo (& var) en lugar de getFoo (var)

prefiero pase por referencia por sólo referencias const, donde estoy tratando de evitar una llamada de constructor de copia.

0

Me parece recordar que en las referencias de C++ no podrían ser nulos ni punteros. Ahora no he usado C++ durante mucho tiempo, así que mi memoria podría estar oxidada.

4

Una ventaja de pasar por referencia es que no pueden ser nulos (a diferencia de los punteros), obviando la necesidad de anular la verificación de todos los parámetros.

+2

GetFoo (* (Foo *) 0); Y cualquier GetFoo (* ptr); para esa materia. No depende cómo se acepta el parámetro, sino cómo se pasa. Creo que Stroustrup (ya sea real o falso) tiene sentido, cuando usas el puntero es más obvio. Por otro lado, creo que es mejor decidir en el lugar que desea ir en el caso particular. –

+0

Maldición, el formateo se comió mis asteriscos! ;-) –

1

Pase por referencia y evite todo el problema del puntero NULL.

0

La diferencia aquí es relativamente menor.

Una referencia no puede ser NULA.

Se puede pasar un nullpointer. Por lo tanto, puede verificar si eso sucede y reaccionar en consecuencia.

Personalmente no puedo pensar en una ventaja real de una de las dos posibilidades.

+0

Por un lado, no considero esta diferencia "menor" - es la diferencia entre un objeto y ningún objeto, después de todo. Además, existe otra diferencia: un puntero puede ser fácilmente inválido (señalar a una dirección aleatoria), mientras que una referencia no puede ser inválida fácilmente. – sbi

0

Me parece una cuestión de gusto personal. En realidad, prefiero pasar por referencia porque los indicadores dan más libertad pero también tienden a causar muchos problemas.

0

El beneficio de un puntero es que no se puede pasar nada, es decir. utilizarlo como si el parámetro era totalmente opcional y no tener una variable de la persona que llama pasa.

Referencias de lo contrario son más seguros, si lo tiene su garantía de existir y tener permiso de escritura (a menos que const por supuesto)

I Creo que es una cuestión de preferencia de lo contrario, pero no me gusta mezclar los dos, ya que creo que hace que el mantenimiento y la legibilidad de tu código sean más difíciles (especialmente si tus 2 funciones tienen el mismo aspecto)

5

Ventajas de pasando por referencia:

  • Obliga al usuario a proporcionar un valor.
  • Menos propenso a errores: maneja la desreferenciación del puntero. No tiene que verificar si hay nulo adentro.
  • Hace que el código de llamada se vea mucho más limpio.

Ventajas a pasar el puntero por valor:

  • Permite a nula que se pasan los parámetros "opcionales". Un poco feo, pero a veces útil.
  • Obliga a la persona que llama a saber qué se está haciendo con el parámetro.
  • Le da al lector la mitad de una pista de lo que se podría hacer con el parámetro sin tener que leer la API.

Desde el fallecimiento de referencia está en la lengua, cualquier parámetro de puntero no pudo conseguir modificado también, y usted no sabe que se están cambiando valores de puntero. He visto API donde se tratan como constantes.Por lo tanto, el paso del puntero no les brinda a los lectores ninguna información con la que puedan contar. Para algunas personas eso podría ser lo suficientemente bueno, pero para mí no lo es.

En realidad, el paso del puntero es simplemente un desorden sucio de C que no tiene otra forma de pasar valores por referencia. C++ tiene una forma, por lo que el truco ya no es necesario.

3

lo recomiendo que considera (puede no ser el mejor para cada situación) el regreso de Foo de la función en lugar de modificar un parámetro. Su prototipo de la función se vería así:

Foo GetFoo() // const (if a member function) 

Como es Parece que el retorno de una bandera de éxito/fracaso, usando una excepción podría ser una mejor estrategia.

Ventajas:

  • a evitar todos los problemas puntero/referencia
  • simplifica la vida de la persona que llama. Puede pasar el valor de retorno a otras funciones sin usar una variable local, por ejemplo.
  • La persona que llama no puede ignorar el estado del error si lanza una excepción.
  • La optimización del valor de retorno significa que puede ser tan eficiente como modificar un parámetro.
+0

La excepción a veces puede ser una mala opción para señalar un error. Mis últimos dos proyectos fueron construidos con la bandera -no-exceptions, dejando la única opción para señalar un error al devolver un valor ... – SadSido

+0

@SadSido: Por supuesto. Dije, "... usar una excepción podría ser una mejor estrategia". Usted dijo: "La excepción a veces puede ser una mala opción ...". Ambas declaraciones son correctas. En general, sin embargo, creo que las excepciones son una mejor solución. –

0

En estos días utilizo las referencias de const para los parámetros de entrada y los indicadores para los parámetros de salida. FWIIW, Google C++ Style Guide recommends the same approach (no siempre estoy de acuerdo con su guía de estilo; por ejemplo, no usan excepciones, lo que generalmente no tiene mucho sentido)

0

Mi preferencia es una referencia. Primero, porque rima. :) También debido a los problemas señalados por otras respuestas: no es necesario desreferencia, y no hay posibilidad de que una referencia sea NULA. Otra razón, que no he mencionado, es que cuando ve un puntero no puede estar seguro de si apunta a la memoria asignada dinámicamente, y puede estar tentado de llamar al delete. Una referencia, por otro lado, prescinde de cualquier ambigüedad con respecto a la gestión de la memoria.

Habiendo dicho eso, hay por supuesto muchos casos cuando es preferible, o incluso necesario, pasar un puntero. Si sabes de antemano que el parámetro es opcional, entonces permitir que sea NULL es muy útil. De manera similar, puede saber de antemano que el parámetro siempre se asigna dinámicamente y que la gestión de la memoria está completa.

Cuestiones relacionadas