2010-04-10 14 views
5

Considere el siguiente código donde devuelvo double& y string&. Funciona bien en el caso de un doble, pero no en el caso de una cadena. ¿Por qué el comportamiento difiere?Devolviendo una referencia en C++

En ambos casos el compilador ni siquiera arroja el Warning: returning address of local variable or temporary ya que estoy devolviendo una referencia.

#include <iostream> 
#include <string> 
using namespace std; 


double &getDouble(){ 
    double h = 46.5; 
    double &refD = h; 
    return refD; 
} 

string &getString(){ 
    string str = "Devil Jin"; 
    string &refStr = str; 
    return refStr; 
} 

int main(){ 
    double d = getDouble(); 
    cout << "Double = " << d << endl; 

    string str = getString(); 
    cout << "String = " << str.c_str() << endl; 

    return 0; 
} 

Salida:

$ ./a.exe 
Double = 46.5 
String = 
+0

Tuve una publicación similar y las respuestas fueron muy útiles. Además, echa un vistazo a los comentarios http://stackoverflow.com/questions/2612709/why-does-this-object-wonk-out-get-deleted – brainydexter

Respuesta

15

Nunca se debe devolver una referencia a una variable local no importa lo que el compilador o deja de hacer. El compilador puede ser engañado fácilmente. no debe basar la corrección de su código en alguna advertencia que no se haya disparado.

La razón por la que no ha aparecido aquí probablemente sea que no está devolviendo literalmente una referencia a una variable local, sino que está devolviendo una variable que hace referencia a una variable local. El compilador probablemente no detecta esta situación algo más compleja. Sólo se detecta cosas como:

string &getString(){ 
    string str = "Devil Jin"; 
    return str; 
} 

El caso de la doble es más simple, ya que no implica la construcción y destruyendo un objeto complejo por lo que en esta situación el análisis de control de flujo del compilador probablemente hizo un mejor trabajo en la detección de el error.

+0

local a la función, obviamente significa. puede devolver una referencia a cualquier cosa que esté fuera de su alcance: datos de miembro, parámetros de función tomados como referencia, etc. – wilhelmtell

+6

En realidad, creo que dice que la advertencia NO se activa incluso en el caso del doble. Entonces el compilador es engañado en ambos casos. La razón por la que funciona con el doble es probable porque la ubicación de h en la pila no se ha sobrescrito. Pruebe esto: en la tienda principal d en un doble y luego llame a un par de otras funciones, luego imprima d. Apuesto a que no sale bien. Sin embargo, en todos los casos, lo que haces es un "comportamiento indefinido". Hay una excelente lección: solo porque da la respuesta correcta no significa que se haya hecho de la manera correcta. ;) –

6

La referencia a double se refiere a una ubicación que aún está físicamente en la memoria pero ya no en la pila. Solo te saldrás con la tuya porque aún no se ha sobrescrito la memoria. Mientras que double es una primitiva, string es un objeto y tiene un destructor que puede estar borrando la cadena interna a una longitud cero cuando se sale del alcance. El hecho de que no está recibiendo basura de su llamada al c_str() parece apoyar eso.

+1

Honestamente, todo esto parece una especulación, y realmente no importa. Cuando devuelve una referencia a una variable local, el compilador puede hacer lo que quiera.Si se siente como tomarse un descanso e ir a la cocina para hacerse un sándwich, eso también está bien para el estándar. Entonces, la respuesta es simple: no devuelva una referencia a menos que la variable no sea local para la función. – wilhelmtell

+0

Por eso dije "salirse con la tuya". No apoyo el comportamiento solo tratando de responder "¿por qué?" –

+0

@wilhelmtell: No es especulación, es lo que sucederá. El problema es una vez que utilizas el valor de retorno, ya que todo tipo de cosas malas pueden suceder: lo mejor que puede suceder es que el programa segfaults; Lo peor es que su programa continúa funcionando hasta que misteriosamente se porta mal o segmenta más tarde. – CMircea

1

GCC solía tener una extensión llamada Named Returns que le permite lograr lo mismo, pero asigna el espacio fuera de la función. Lamentablemente, ya no existe; No estoy seguro de por qué lo sacaron

+0

+1 para la referencia histórica, incluso si ya no es válida. Y bienvenido a SO. –

0

Caso clásico de una referencia colgante con respecto a C++. La variable doble no estaba en la pila de llamadas mientras que la referencia de retorno intentaba acceder invocando al compilador para establecer los indicadores de protección. Sin embargo, String tiene un mecanismo explícito de recolección de basura que permite que su compilador pase por alto el escenario.