En C ++, ¿por qué no deberían ser referencias todos los parámetros de la función?

7

Actualmente estoy aprendiendo C ++ de la 5ta edición de C ++ Primer. El capítulo del libro sobre funciones establece que solo se deben pasar como referencias los objetos grandes (relativamente grandes como relativos a las cadenas de bibliotecas estándar, pero los "tipos primitivos" como int o char no) deben pasarse como referencias.

Por ejemplo, la siguiente función sirve como ejemplo en el libro sobre cuándo usar los parámetros de referencia:

string::size_type find_char(const string &s, char c, string::size_type &occurs){
    /* code to return the first occurrence of c in s, occurs contains total number of occurrences */
}

Uno de los ejercicios pregunta cuál es la razón para que cada parámetro sea más allá de su tipo base. Entiendo por qué la cadena es una referencia (para evitar que se copie para mejorar el rendimiento) y por qué es constante (ya que no es necesario modificarla). También entiendo por qué el parámetro occurs es una referencia (ya que el valor de la variable original debe cambiarse).

Lo que no entiendo es por qué c no es una referencia (constante). Sé que contiene un objeto muy pequeño, pero, ¿no sería la prevención de la copia de cualquier objeto, no importa cuán pequeño sea, una mejora en el rendimiento?

Si hubiera algún inconveniente en el uso de referencias, entonces tendría sentido. Pero, no tengo idea de lo que sería un inconveniente. Además, sé que se copiaría la dirección de un puntero, pero como las referencias no se pueden cambiar, me imagino que la función llamada podría acceder a la variable original exactamente de la misma manera que lo hace la persona que llama (asumiendo que la variable está declarada en el llamante).

¿Por qué debería usarse un parámetro que no sea de referencia, especialmente en casos como el ejemplo anterior, a menos que el parámetro deba modificarse para la implementación de la función sin devolver el valor cambiado?

NOTA: el cuerpo de la función está incluido en el libro, pero se elimina de esta pregunta por brevedad.

    
pregunta john01dav 14.05.2016 - 01:47

3 respuestas

7

Para los tipos básicos, los parámetros de referencia generalmente no ofrecen ningún atributo de rendimiento.

Memoria

Por ejemplo, en su ejemplo, el char requiere un solo byte. Un tipo de referencia requiere un puntero, que generalmente es de 4 u 8 bytes, donde el tamaño de un puntero se relaciona directamente con el tipo de ejecutable que está utilizando (4 bytes para 32 bits, 8 bytes para 64 bits), debido al tamaño del registro de memoria.

Para la mayoría de las máquinas modernas, esto se pasará ya sea en la pila o en un registro, como un solo valor. Así que realmente no hace ninguna diferencia.

Time

Copiar un tipo básico ( char , int , long ) suele ser una única instrucción MOV . La configuración del puntero utilizado en un parámetro de referencia es, de nuevo, generalmente una única instrucción MOV .

    
respondido por el Steven Burnap 14.05.2016 - 02:31
6

El uso de referencias tiene dos inconvenientes:

  1. El uso de referencias permite un alias adicional. Esto significa que el compilador debe volver a cargar y almacenar el valor con más frecuencia, a menos que pueda determinar de alguna otra manera que un valor no se lee / escribe entre dos usos en el código que ve.

  2. Cada dirección indirecta significa lectura adicional de memoria.

En muchos casos, estos dos efectos dominan cualquier ahorro potencial al no tener que copiar el argumento, incluso si el tipo es ligeramente más grande que un puntero. No es que exista tal ahorro para tipos trivialmente copiables que no sean más grandes que un puntero.

Como nota aparte, desde C ++ 17 debe evitar pasar un const std::string& a favor de un std::string_view :

Este último es más flexible y eficiente, ya que no asigna la cadena, reduce el direccionamiento indirecto y no tiene una política de asignación.

    
respondido por el Deduplicator 24.08.2017 - 12:00
-1

Entiendo que quiere "encapsular" los argumentos que pasa a una función; la función debería poder modificar solo lo que usted desea que modifique. Pasar siempre una referencia y poner const delante de ella es tedioso y propenso a errores.

Además, siempre pasar referencias significaría crear una variable local para cada argumento de función (en lugar de pasar expresiones).

Además, pasar una referencia significa que el compilador probablemente necesitará generar código para la desreferenciación en la función llamada. Para algunas ABI, pasar una referencia puede ser significativamente más lento.

    
respondido por el martinkunev 14.05.2016 - 13:54

Lea otras preguntas en las etiquetas