Pasar por referencia y valor en C++

6 minutos de lectura

avatar de usuario
usuario36064

Me gustaría aclarar las diferencias entre por valor y por referencia.

Hice un dibujo:

Ingrese la descripción de la imagen aquí

Entonces, para pasar por valor, se crea una copia de un objeto idéntico con una referencia diferente, y a la variable local se le asigna la nueva referencia, para que apunte a la nueva copia.

¿Cómo debo entender lo siguiente?

Si la función modifica ese valor, las modificaciones también aparecen dentro del alcance de la función que llama, tanto para pasar por valor como por referencia.

avatar de usuario
Johannes Schaub – litb

Creo que se genera mucha confusión al no comunicar lo que se entiende por pasado por referencia. Cuando algunas personas dicen pasar por referencia por lo general, no se refieren al argumento en sí, sino más bien el objeto al que se hace referencia. Otros dicen que pasar por referencia significa que el objeto no se puede cambiar en el destinatario. Ejemplo:

struct Object {
    int i;
};

void sample(Object* o) { // 1
    o->i++;
}

void sample(Object const& o) { // 2
    // nothing useful here :)
}

void sample(Object & o) { // 3
    o.i++;
}

void sample1(Object o) { // 4
    o.i++;
}

int main() {
    Object obj = { 10 };
    Object const obj_c = { 10 };

    sample(&obj); // calls 1
    sample(obj) // calls 3
    sample(obj_c); // calls 2
    sample1(obj); // calls 4
}

Algunas personas dirían que 1 y 3 se pasan por referencia, mientras que 2 se pasan por valor. Otro grupo de personas dice que todo menos el último se pasa por referencia, porque el objeto en sí no se copia.

Me gustaría dibujar una definición de eso aquí lo que pretendo ser pasar por referencia. Puede encontrar una descripción general aquí: Diferencia entre pasar por referencia y pasar por valor. El primero y el último se pasan por valor, y los dos del medio se pasan por referencia:

    sample(&obj);
       // yields a `Object*`. Passes a *pointer* to the object by value. 
       // The caller can change the pointer (the parameter), but that 
       // won't change the temporary pointer created on the call side (the argument). 

    sample(obj)
       // passes the object by *reference*. It denotes the object itself. The callee
       // has got a reference parameter.

    sample(obj_c);
       // also passes *by reference*. the reference parameter references the
       // same object like the argument expression. 

    sample1(obj);
       // pass by value. The parameter object denotes a different object than the 
       // one passed in.

Voto por la siguiente definición:

Un argumento (1.3.1) se pasa por referencia si y solo si el parámetro correspondiente de la función que se llama tiene tipo de referencia y el parámetro de referencia se vincula directamente con la expresión del argumento (8.5.3/4). En todos los demás casos, tenemos que ver con pasar por valor.

Eso significa que lo siguiente es pasar por valor:

void f1(Object const& o);
f1(Object()); // 1

void f2(int const& i);
f2(42); // 2

void f3(Object o);
f3(Object());     // 3
Object o1; f3(o1); // 4

void f4(Object *o);
Object o1; f4(&o1); // 5

1 pasa por valor, porque no está directamente vinculado. La implementación puede copiar el temporal y luego vincular ese temporal a la referencia. 2 se pasa por valor, porque la implementación inicializa un temporal del literal y luego se vincula a la referencia. 3 es pasar por valor, porque el parámetro no tiene tipo de referencia. 4 pasa por valor por la misma razón. 5 se pasa por valor porque el parámetro no tiene tipo de referencia. Los siguientes casos se pasan por referencia (por las reglas de 8.5.3/4 y otros):

void f1(Object *& op);
Object a; Object *op1 = &a; f1(op1); // 1

void f2(Object const& op);
Object b; f2(b); // 2

struct A { };
struct B { operator A&() { static A a; return a; } };
void f3(A &);
B b; f3(b); // passes the static a by reference

  • ¿Puede explicar “el parámetro de referencia se une directamente a la expresión del argumento”

    – yesraaj

    8 de enero de 2009 a las 11:30

  • si el argumento no tiene el mismo tipo o no es una clase derivada del parámetro, y no tiene un operador de conversión al tipo del parámetro, entonces la expresión del argumento sí lo tiene. no enlazar directamente con el parámetro de referencia.

    – Johannes Schaub – litb

    8 de enero de 2009 a las 11:37

  • también si el argumento es un valor r (como el literal entero 42 en el ejemplo). puede encontrar la definición detallada en el estándar

    – Johannes Schaub – litb

    8 de enero de 2009 a las 11:39

  • en “void f1(Objeto const& o); f1(Objeto());” ¿Por qué se permite que el impl copie el temporal?

    Iraimbilanja

    8 de enero de 2009 a las 17:36

  • Iraimbilanja, porque la norma dice eso (léase 8.5.3p5). la implementación puede crear una copia, pero no tener Para hacer eso. de hecho, al pasar el rvalue (el Object()) al constructor de copia de Object, no debe hacerlo, para evitar la repetición infinita (tendría que copiar nuevamente: p)

    – Johannes Schaub – litb

    8 de enero de 2009 a las 18:33

avatar de usuario
Fuego

Al pasar por valor:

void func(Object o);

y luego llamando

func(a);

vas a construir un Object en la pila, y dentro de la implementación de func será referenciado por o. Esto todavía podría ser una copia superficial (las partes internas de a y o podría apuntar a los mismos datos), por lo que a podría ser cambiado. Sin embargo, si o es una copia profunda de adespués a no cambiará.

Al pasar por referencia:

void func2(Object& o);

y luego llamando

func2(a);

solo estarás dando una nueva forma de referenciar a. “a” y “o” son dos nombres para el mismo objeto. Cambiar o en el interior func2 hará que esos cambios sean visibles para la persona que llama, que conoce el objeto por el nombre “a“.

No estoy seguro de haber entendido bien tu pregunta. Es un poco confuso. Sin embargo, lo que podría confundirte es lo siguiente:

  1. Al pasar por referencia, se pasa una referencia al mismo objeto a la función que se llama. Cualquier cambio en el objeto se reflejará en el objeto original y, por lo tanto, la persona que llama lo verá.

  2. Al pasar por valor, se llamará al constructor de copia. El constructor de copia predeterminado solo hará una copia superficial, por lo tanto, si la función llamada modifica un número entero en el objeto, la función que llama no lo verá, pero si la función cambia una estructura de datos a la que apunta un puntero dentro del objeto. , entonces esto será visto por la persona que llama debido a la copia superficial.

Puede que haya entendido mal tu pregunta, pero pensé en darle una puñalada de todos modos.

Como lo analizo, esas palabras están mal. Debería decir “Si la función modifica ese valor, las modificaciones también aparecen dentro del alcance de la función de llamada al pasar por referencia, pero no al pasar por valor”.

Mi comprensión de las palabras “Si la función modifica ese valor, las modificaciones también aparecen dentro del alcance de la función de llamada tanto para pasar por valor como por referencia” es que son un error.

Las modificaciones realizadas en una función llamada son no en el ámbito de la función de llamada al pasar por valor.

O ha escrito mal las palabras citadas o se han extraído de cualquier contexto que hizo que lo que parece estar mal, correcto.

¿Podría asegurarse de haber citado correctamente su fuente y si no hay errores, proporcione más del texto que rodea esa declaración en el material de origen?

¿Ha sido útil esta solución?