valores valor retornan referencia que por paso parametros funciones ejercicios datos con c++ variables pass-by-reference constants pass-by-value

c++ - retornan - parametros por valor y por referencia java



¿Es mejor en C++ pasar por valor o pasar por referencia constante? (10)

¿Es mejor en C ++ pasar por valor o pasar por referencia constante?

Me pregunto cuál es la mejor práctica. Me doy cuenta de que el paso por referencia constante debe proporcionar un mejor rendimiento en el programa porque no está haciendo una copia de la variable.


Como regla general, el valor para los tipos que no son de clase y la referencia constante para las clases. Si una clase es realmente pequeña, probablemente sea mejor pasarla por valor, pero la diferencia es mínima. Lo que realmente quieres evitar es pasar una clase gigantesca por valor y duplicarlo todo. Esto hará una gran diferencia si estás pasando, digamos, un std :: vector con bastantes elementos.


Como regla general, pasar por referencia constante es mejor. Pero si necesita modificar su argumento de función localmente, debería usar mejor pasar por valor. Para algunos tipos básicos, el rendimiento en general es el mismo tanto para pasar por valor como por referencia. En realidad, la referencia está representada internamente por el puntero, es por eso que puede esperar, por ejemplo, que para el puntero ambos pases sean los mismos en términos de rendimiento, o incluso pasar por valor puede ser más rápido debido a una innecesaria referencia.


Como se ha señalado, depende del tipo. Para los tipos de datos incorporados, es mejor pasar por valor. Incluso algunas estructuras muy pequeñas, como un par de ints, pueden funcionar mejor al pasar por valor.

Aquí hay un ejemplo, suponga que tiene un valor entero y desea pasarlo a otra rutina. Si ese valor se ha optimizado para ser almacenado en un registro, entonces si desea pasarlo como referencia, primero debe almacenarse en la memoria y luego un puntero a esa memoria colocada en la pila para realizar la llamada. Si se pasó por valor, todo lo que se requiere es el registro insertado en la pila. (Los detalles son un poco más complicados que los diferentes sistemas de llamadas y CPU).

Si está realizando la programación de plantillas, normalmente se ve obligado a pasar siempre por la referencia constante, ya que no sabe qué tipos se están pasando. Las penalizaciones de aprobación por pasar algo malo son mucho peores que las penalizaciones de aprobar un tipo incorporado por const. ref.


Depende del tipo. Usted está agregando la pequeña sobrecarga de tener que hacer una referencia y desreferencia. Para los tipos con un tamaño igual o menor que los punteros que utilizan el ctor de copia predeterminado, probablemente sería más rápido pasar por valor.


Diferencia simple: - En la función tenemos parámetros de entrada y salida, por lo tanto, si su parámetro de entrada y salida es el mismo, utilice la función de llamada por referencia, si los parámetros de entrada y salida son diferentes, es mejor utilizar la llamada por valor.

Ejemplo de void amount(int account , int deposit , int total )

Parámetro de entrada: cuenta, parámetro de salida de depósito: total

entrada y salida es diferente uso llamada por vara

  1. void amount(int total , int deposit )

entrada total de salida de depósito total


Esto es con lo que normalmente trabajo al diseñar la interfaz de una función que no es de plantilla:

  1. Pase por valor si la función no desea modificar el parámetro y el valor es barato de copiar (int, double, float, char, bool, etc.) Tenga en cuenta que std :: string, std :: vector, y el resto de los contenedores en la biblioteca estándar NO son

  2. Pase por el puntero de const si el valor es costoso de copiar y la función no desea modificar el valor apuntado y NULL es un valor que maneja la función.

  3. Pase el puntero no constante si el valor es costoso de copiar y la función desea modificar el valor apuntado y NULL es un valor que la función maneja.

  4. Paso por referencia constante cuando el valor es costoso de copiar y la función no desea modificar el valor al que se hace referencia y NULL no sería un valor válido si se usara un puntero en su lugar.

  5. Pase por referencia no constante cuando el valor es costoso de copiar y la función desea modificar el valor referido y NULL no sería un valor válido si en su lugar se usara un puntero.


Parece que tienes tu respuesta. Pasar por valor es costoso, pero le da una copia para trabajar si la necesita.


Pase por valor para tipos pequeños.

Pase por referencias constantes para tipos grandes (la definición de grande puede variar entre máquinas) PERO, en C ++ 11, pase por valor si va a consumir los datos, ya que puede explotar la semántica de movimientos. Por ejemplo:

class Person { public: Person(std::string name) : name_(std::move(name)) {} private: std::string name_; };

Ahora el código de llamada haría:

Person p(std::string("Albert"));

Y solo se crearía un objeto y se name_ directamente al nombre de name_ en la clase Person . Si pasa por referencia de const, se deberá hacer una copia para ponerlo en name_ .


Edición: Nuevo artículo de Dave Abrahams en cpp-next:

¿Quieres velocidad? Pase por valor.

El paso por valor para las estructuras donde la copia es barata tiene la ventaja adicional de que el compilador puede suponer que los objetos no son alias (no son los mismos objetos). Usando paso por referencia, el compilador no puede asumir eso siempre. Ejemplo simple:

foo * f; void bar(foo g) { g.i = 10; f->i = 2; g.i += 5; }

el compilador puede optimizarlo en

g.i = 15; f->i = 2;

ya que sabe que f y g no comparten la misma ubicación. si g era una referencia (foo &), el compilador no podría haber asumido eso. ya que gi podría tener un alias de f-> i y tener que tener un valor de 7. por lo que el compilador tendría que recuperar el nuevo valor de gi de la memoria.

Para reglas más prácticas, aquí hay un buen conjunto de reglas que se encuentran en el artículo de Move Constructors (lectura muy recomendada).

  • Si la función pretende cambiar el argumento como un efecto secundario, tómelo por referencia no constante.
  • Si la función no modifica su argumento y el argumento es de tipo primitivo, tómelo por valor.
  • De lo contrario, tómelo por referencia constante, excepto en los siguientes casos
    • Si la función necesitaría hacer una copia de la referencia constante de todos modos, tómela por valor.

"Primitivo" anterior significa básicamente tipos de datos pequeños que tienen unos pocos bytes de largo y no son polimórficos (iteradores, objetos de función, etc.) o costosos de copiar. En ese papel, hay otra regla. La idea es que a veces uno quiere hacer una copia (en caso de que no se pueda modificar el argumento), y otras veces no quiere (en caso de que uno quiera usar el argumento en sí mismo en la función si el argumento era temporal de todos modos). , por ejemplo). El documento explica en detalle cómo se puede hacer eso. En C ++ 1x, esta técnica se puede utilizar de forma nativa con soporte de idioma. Hasta entonces, me gustaría ir con las reglas anteriores.

Ejemplos: para hacer una cadena en mayúsculas y devolver la versión en mayúsculas, siempre se debe pasar por valor: de todos modos, se debe tomar una copia (no se puede cambiar la referencia de const directamente), por lo que es mejor hacerlo lo más transparente posible. la persona que llama y haga esa copia antes de tiempo para que la persona que llama pueda optimizar tanto como sea posible, como se detalla en ese documento:

my::string uppercase(my::string s) { /* change s and return it */ }

Sin embargo, si no necesita cambiar el parámetro de todos modos, tómelo por referencia a const:

bool all_uppercase(my::string const& s) { /* check to see whether any character is uppercase */ }

Sin embargo, si el propósito del parámetro es escribir algo en el argumento, entonces páselo por referencia no constante

bool try_parse(T text, my::string &out) { /* try to parse, write result into out */ }


Se solía recomendar generalmente la práctica recomendada 1 usar la referencia paso a paso para todos los tipos , excepto para los tipos incorporados ( char , int , double , etc.), para los iteradores y para los objetos de función (lambdas, clases derivadas de std::*_function ).

Esto fue especialmente cierto antes de la existencia de semántica de movimientos . El motivo es simple: si se pasa por valor, se debe hacer una copia del objeto y, a excepción de los objetos muy pequeños, esto siempre es más costoso que pasar una referencia.

Con C ++ 11, hemos ganado movimiento semántico . En pocas palabras, la semántica de movimiento permite que, en algunos casos, un objeto se pueda pasar "por valor" sin copiarlo. En particular, este es el caso cuando el objeto que está pasando es un rvalue .

En sí mismo, mover un objeto sigue siendo al menos tan costoso como pasar por referencia. Sin embargo, en muchos casos, una función copiará internamente un objeto de todos modos, es decir, tomará posesión del argumento. 2

En estas situaciones tenemos la siguiente compensación (simplificada):

  1. Podemos pasar el objeto por referencia, luego copiarlo internamente.
  2. Podemos pasar el objeto por valor.

"Pasar por valor" todavía hace que el objeto se copie, a menos que el objeto sea un valor nominal. En el caso de un valor de r, el objeto se puede mover en su lugar, de modo que el segundo caso de repente ya no es "copiar, luego mover" sino "mover, luego (potencialmente) mover de nuevo".

Para los objetos grandes que implementan constructores de movimientos adecuados (como vectores, cadenas ...), el segundo caso es mucho más eficiente que el primero. Por lo tanto, se recomienda usar pasar por valor si la función se apropia del argumento y si el tipo de objeto admite el movimiento eficiente .

Una nota histórica:

De hecho, cualquier compilador moderno debería ser capaz de entender cuándo pasar un valor es costoso, y convertir implícitamente la llamada para usar una referencia constante si es posible.

En teoria. En la práctica, los compiladores no siempre pueden cambiar esto sin romper la interfaz binaria de la función. En algunos casos especiales (cuando la función está en línea), la copia realmente será eliminada si el compilador puede darse cuenta de que el objeto original no se cambiará a través de las acciones en la función.

Pero en general, el compilador no puede determinar esto, y el advenimiento de la semántica de movimientos en C ++ ha hecho que esta optimización sea mucho menos relevante.

1 Por ejemplo, en Scott Meyers, Efectivo C ++ .

2 Esto es especialmente cierto para los constructores de objetos, que pueden tomar argumentos y almacenarlos internamente para formar parte del estado del objeto construido.