Pregunta

#include <iostream>
using namespace std;

class X {
        public:
                X() {
                        cout<<"Cons"<<endl;
                }
                X(const X& x){
                        cout<<"Copy"<<endl;
                }
                void operator=(const X& x){
                        cout<<"Assignment called";
                }
};

X& fun() {
        X s;
        return s;
}

int main(){
        X s = fun();
        return 0;
}

Este código también llama al constructor de copia. ¿Por qué funciona esto? Recuerdo que la primera vez que ejecuté este programa, seg falla. Pero después de un tiempo, comenzó a llamar a esta copia contras. y ahora funciona !! Wierd.

Pero si sustituyo, fun () como sigue:

X fun() {
        X s;
        return s;
}

Entonces copia contras. no se llama Pensé que la copia está en contra. Sería llamado en este caso. Pero como lo señaló @ flyfishr64, RVO entra en juego aquí. Pero todavía no explica el caso en el que estoy devolviendo una referencia. Creo que siempre debería segfault.

¿Alguna explicación?

¿Fue útil?

Solución

Para expandir la respuesta de @flyfishr64

El constructor de copia se invoca aquí porque esto:

X s = fun();

es una inicialización. Está utilizando fun () para construir el objeto, no invocar el constructor predeterminado. Es equivalente a:

X s(fun());

Los " Contras " Lo que ves impreso es para la instancia en fun (). Consulte este artículo: operador de asignación en C ++ para obtener más información.

Otros consejos

Esto devuelve la referencia a un objeto en la pila que no existe una vez que se devuelve el método: la pila está desenrollada, la memoria todavía está allí, pero no debería usarla

X& fun() {
        X s;
        return s;
}

Cuando cambias eso a:

X fun() {
        X s;
        return s;
}

Ahora estás devolviendo una copia. Si el compilador es lo suficientemente inteligente, podría hacerlo:

X fun() {
    return X();
}

En ese caso, el X se asigna directamente en la pila de personas que llaman, por lo que no se requiere copia.

Si segfault o no depende de si está accediendo a la memoria no válida.

En su ejemplo, no accede a ningún valor de la estructura. Para ver segfault, primero mantenga una referencia que devolvió con fun () agregue algunas variables en la estructura X y luego de regresar de fun () call otro método que asigna internamente alguna memoria en la pila (esto debería sobrescribir la memoria original utilizada por X en fun ) y almacena algunos valores en la pila (preferiblemente 0's). Después de que este segundo método devuelva, intente imprimir los valores de X utilizando la referencia original devuelta de fun ...

En este código:

X fun() {
        X s;
        return s;
}

no se está llamando al constructor de copia debido a la Optimización del valor de retorno, que permite al compilador omitir la creación de la variable local 's' y construir X directamente en la variable devuelta.

Puede leer más sobre RVO aquí

Cuando devuelves una referencia a una variable local como esa, estás invocando un comportamiento no definido.

Sucede que funciona en este caso porque ninguna de las funciones de clase X usa el puntero this , por lo que no importa que ya no sea válido.

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