c++ - referencias - ¿Por qué pasar por referencia constante en lugar de por valor?
paso por referencia en c (8)
Por lo que entiendo: cuando pasa por valor, la función hace una copia local del argumento pasado y lo usa; cuando la función finaliza, sale del alcance. Cuando pasa por referencia constante, la función utiliza una referencia al argumento pasado que no se puede modificar. No entiendo, sin embargo, por qué uno elegiría uno sobre el otro, excepto en una situación donde un argumento necesita ser modificado y devuelto. Si tenía una función nula donde no se devuelve nada, ¿por qué elegir una sobre la otra?
EDITAR: Así que, básicamente, pasar por referencia de referencia evita copiar el objeto. Entonces, ¿en qué situaciones está bien copiar el objeto? Quiero decir, ¿por qué no usar referencias continuas todo el tiempo si optimiza el rendimiento todo el tiempo?
A veces, hacer una copia de un objeto puede ser costoso y, por lo tanto, la referencia paso a paso evitará tener que hacer esa copia. De lo contrario, diría que simplemente debe pasar por valor si eso es lo que se requiere semánticamente.
Debido a los beneficios de rendimiento que obtendrá. Digamos que tienes un objeto grande (en términos de tamaño en bytes). Ahora, si pasa este objeto por valor a una función, se necesita crear una copia innecesaria de esto, sin embargo, puede obtener el mismo efecto pasando una referencia constante a ese objeto sin crear una copia. Dado que una referencia normalmente se almacena como un puntero debajo de las capuchas, el costo de pasar una referencia es solo sizeof(pointer)
.
Hacer una copia de un objeto podría afectar en gran medida el rendimiento en algunos casos. Considere una función cuyo argumento será std::vector<long>
y desea pasar vector con 1 millón de elementos. En este caso, querrás usar la referencia constante sobre pasar por valor. En esta pregunta SO , puede encontrar una regla general simple para su pregunta.
Hay dos consideraciones principales. Uno es el costo de copiar el objeto pasado y el segundo son las suposiciones que el compilador puede hacer cuando el objeto es un objeto local.
Por ejemplo, en la primera forma, en el cuerpo de f
no se puede suponer que a
y b
no hacen referencia al mismo objeto; por lo tanto, el valor de a
debe volver a leerse después de cualquier escritura en b
, por las dudas. En la segunda forma, a
no se puede cambiar mediante una escritura a b
, ya que es local para la función, por lo que estas relecturas son innecesarias.
void f(const Obj& a, Obj& b)
{
// a and b could reference the same object
}
void f(Obj a, Obj& b)
{
// a is local, b cannot be a reference to a
}
Por ejemplo: en el primer ejemplo, el compilador puede suponer que el valor de un objeto local no cambia cuando se realiza una llamada no relacionada. Sin información sobre h
, el compilador puede no saber si h
no cambia un objeto al que esa función tiene una referencia (a través de un parámetro de referencia). Por ejemplo, ese objeto podría ser parte de un estado global que se modifica por h
.
void g(const Obj& a)
{
// ...
h(); // the value of a might change
// ...
}
void g(Obj a)
{
// ...
h(); // the value of a is unlikely to change
// ...
}
Desafortunadamente, este ejemplo no es de hierro fundido. Es posible escribir una clase que, por ejemplo, se agregue un puntero a un objeto de estado global en su constructor, de modo que incluso un objeto local de tipo de clase pueda ser alterado por una llamada a función global. A pesar de esto, todavía hay más oportunidades para optimizaciones válidas para objetos locales ya que no pueden ser alias directamente por referencias pasadas u otros objetos preexistentes.
Se debe elegir pasar un parámetro por referencia const
donde la semántica de las referencias es realmente necesaria, o como una mejora en el rendimiento solo si el costo del aliasing potencial se compensa con el costo de copiar el parámetro.
Para evitar hacer una copia innecesaria , lo que mejora el rendimiento.
Pasar argumentos por valor y copiarlos así puede ser costoso: las referencias de referencias evitan ese paso costoso al tiempo que prometen al llamante que el objeto no se cambiará.
Por lo general, los tipos fundamentales ( int
, double
, ...) se pasan por valor, mientras que los tipos de clase se pasan por referencia constante.
Sin embargo, puede haber exceptions en las que el valor de paso por valor para los tipos de clase puede ser beneficioso.
Pasar un argumento por valor tiene la sobrecarga de una copia del objeto que se pasa a la función.
Tal vez un objeto no se puede copiar y tus opciones son limitadas.
Una matriz no se puede pasar por valor, por lo que este es un buen momento para usar un puntero const.