Pregunta

¿Cuáles son los beneficios de pasar por puntero sobre pasar por referencia en C ++?

Últimamente, he visto varios ejemplos que eligieron pasar argumentos de función por punteros en lugar de pasar por referencia. ¿Hay beneficios al hacer esto?

Ejemplo:

func(SPRITE *x);

con una llamada de

func(&mySprite);

vs.

func(SPRITE &x);

con una llamada de

func(mySprite);
¿Fue útil?

Solución

Un puntero puede recibir un parámetro NULL, un parámetro de referencia no. Si alguna vez existe la posibilidad de que desee pasar "ningún objeto", utilice un puntero en lugar de una referencia.

Además, pasar por puntero le permite ver explícitamente en el sitio de la llamada si el objeto se pasa por valor o por referencia:

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);

Otros consejos

Pasando por puntero

  • La persona que llama tiene que tomar la dirección - > no transparente
  • Se puede proporcionar un valor 0 para significar nothing . Esto se puede usar para proporcionar argumentos opcionales.

Pasar por referencia

  • La persona que llama solo pasa el objeto - > transparente. Tiene que usarse para la sobrecarga del operador, ya que la sobrecarga para los tipos de puntero no es posible (los punteros son tipos incorporados). Por lo tanto, no puede hacer string s = & amp; str1 + & amp; str2; con punteros.
  • No hay valores 0 posibles - > La función llamada no tiene que verificarlos
  • La referencia a const también acepta temporarios: void f (const T & amp; t); ... f (T (a, b, c)); , los punteros no se pueden usar así, ya que no se puede tomar la dirección de un temporal.
  • Por último, pero no menos importante, las referencias son más fáciles de usar: > menos posibilidades de errores.

Allen Holub's '' Suficiente cuerda para dispararte en el pie '' enumera las siguientes 2 reglas:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

Enumera varias razones por las que se agregaron referencias a C ++:

  • son necesarios para definir constructores de copias
  • son necesarios para sobrecargas del operador
  • Las referencias
  • const le permiten tener una semántica de paso por valor mientras evita una copia

Su punto principal es que las referencias no deben usarse como parámetros de 'salida' porque en el sitio de la llamada no hay indicación de si el parámetro es una referencia o un parámetro de valor. Por lo tanto, su regla es usar solo referencias const como argumentos.

Personalmente, creo que esta es una buena regla general, ya que deja más claro cuándo un parámetro es un parámetro de salida o no. Sin embargo, aunque personalmente estoy de acuerdo con esto en general, me dejo influenciar por las opiniones de otros en mi equipo si ellos defienden los parámetros de salida como referencias (a algunos desarrolladores les gustan inmensamente).

Me gusta el razonamiento de un artículo de " cplusplus.com: "

  
      
  1. Pasa por valor cuando la función no quiere modificar el parámetro y el valor es fácil de copiar (ints, doubles, char, bool, etc ... tipos simples. std :: string, std :: vector, y todos los demás contenedores STL NO son tipos simples).

  2.   
  3. Pase por puntero constante cuando el valor es costoso de copiar Y la función no quiere modificar el valor señalado Y NULO es un valor válido y esperado que maneja la función.

  4.   
  5. Pase por puntero no constante cuando el valor es costoso de copiar Y la función quiere modificar el valor apuntado Y NULO es un valor válido y esperado que maneja la función.

  6.   
  7. Pase por referencia constante cuando el valor es costoso de copiar Y la función no quiere modificar el valor al que se hace referencia AND NULL no sería un valor válido si se utilizara un puntero.

  8.   
  9. Pase por referencia no cont cuando el valor es costoso de copiar Y la función quiere modificar el valor al que se hace referencia AND NULL no sería un valor válido si se utilizara un puntero.

  10.   
  11. Al escribir funciones de plantilla, no hay una respuesta clara porque hay algunas compensaciones a considerar que están más allá del alcance de esta discusión, pero es suficiente decir que la mayoría de las funciones de plantilla toman sus parámetros por Sin embargo, debido a que la sintaxis del iterador es similar a la de los punteros (asterisco para `` desreferenciar ''), cualquier función de plantilla que espere iteradores como argumentos también aceptará punteros por defecto (y no comprobará NULL ya que El concepto de iterador NULL tiene una sintaxis diferente).

  12.   
     

http://www.cplusplus.com/articles/z6vU7k9E/

Lo que deduzco de esto es que la principal diferencia entre elegir usar un puntero o un parámetro de referencia es si NULL es un valor aceptable. Eso es todo.

Si el valor es input, output, modificable, etc., debe estar en la documentación / comentarios sobre la función, después de todo.

Aclaraciones a las publicaciones anteriores:


Las referencias son NO una garantía de obtener un puntero no nulo. (Aunque a menudo los tratamos como tales).

Si bien es un código horriblemente malo, como en el caso del código malo de la leñera, lo siguiente compilará & amp; ejecutar: (al menos en mi compilador.)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

El verdadero problema que tengo con las referencias reside en otros programadores, en adelante denominados IDIOTS , que asignan en el constructor, desasignan en el destructor, y no proporciona un constructor de copia u operador = ().

De repente hay un mundo de diferencia entre foo (BAR bar) y foo (BAR & amp; bar) . (Se invoca la operación automática de copia a nivel de bit. La desasignación en el destructor se invoca dos veces).

Afortunadamente, los compiladores modernos recogerán esta doble asignación del mismo puntero. Hace 15 años, no lo hicieron. (Bajo gcc / g ++, use setenv MALLOC_CHECK_ 0 para volver a visitar las viejas formas.) Como resultado, bajo DEC UNIX, la misma memoria se asigna a dos objetos diferentes. Mucha diversión de depuración allí ...


Más prácticamente:

  • Las referencias ocultan que está cambiando los datos almacenados en otro lugar.
  • Es fácil confundir una referencia con un objeto copiado.
  • ¡Los punteros lo hacen obvio!

No realmente. Internamente, el paso por referencia se realiza esencialmente pasando la dirección del objeto referenciado. Por lo tanto, realmente no hay ganancias de eficiencia al pasar un puntero.

Pasar por referencia tiene un beneficio, sin embargo. Se garantiza que tendrá una instancia de cualquier objeto / tipo que se pase. Si pasa un puntero, corre el riesgo de recibir un puntero NULL. Al usar el paso por referencia, está empujando un control NULL implícito hasta un nivel para la persona que llama de su función.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top