2011-01-10 16 views
80

¿Es correcto el siguiente código (func1()) si tiene que devolver i? Recuerdo haber leído en algún lado que hay un problema al devolver la referencia a una variable local. ¿Cómo es diferente de func2()?C++ Referencia de devolución a la variable local

int& func1() 
{ 
    int i; 
    i = 1; 
    return i; 
} 

int* func2() 
{ 
    int* p; 
    p = new int; 
    *p = 1; 
    return p; 
} 
+1

No es correcto. –

+1

Si cambia func1() para usar memoria asignada dinámicamente, entonces son lo mismo :-) 'int & i = * new int;' –

+1

@Martin ¿Quién lo eliminará? – balki

Respuesta

142

Este fragmento de código:

int& func1() 
{ 
    int i; 
    i = 1; 
    return i; 
} 

no funcionará porque usted está volviendo un alias (una referencia) a un objeto con un tiempo de vida limitado al ámbito de la llamada de función. Eso significa que una vez que func1() regresa, int i muere, haciendo que la referencia devuelta por la función no tenga valor porque ahora se refiere a un objeto que no existe.

int main() 
{ 
    int& p = func1(); 
    /* p is garbage */ 
} 

La segunda versión funciona porque la variable se asigna en la tienda libre, que no está ligada a la vida útil de la llamada a la función. Sin embargo, usted es responsable de delete ing el int asignado.

int* func2() 
{ 
    int* p; 
    p = new int; 
    *p = 1; 
    return p; 
} 

int main() 
{ 
    int* p = func2(); 
    /* pointee still exists */ 
    delete p; // get rid of it 
} 

Normalmente le envuelva el puntero en alguna RAII clase y/o una función de fábrica, por lo que no tiene que delete usted mismo.

En cualquiera de los casos, sólo puede devolver el valor en sí (aunque reconozco el ejemplo que nos ha facilitado probablemente fue inventado):

int func3() 
{ 
    return 1; 
} 

int main() 
{ 
    int v = func3(); 
    // do whatever you want with the returned value 
} 

Tenga en cuenta que está perfectamente bien para volver grandes objetos de la misma manera func3() vuelve primitiva los valores, porque casi todos hoy en día compilador implementa algún tipo de return value optimization:

class big_object 
{ 
public: 
    big_object(/* constructor arguments */); 
    ~big_object(); 
    big_object(const big_object& rhs); 
    big_object& operator=(const big_object& rhs); 
    /* public methods */ 
private: 
    /* data members */ 
}; 

big_object func4() 
{ 
    return big_object(/* constructor arguments */); 
} 

int main() 
{ 
    // no copy is actually made, if your compiler supports RVO 
    big_object o = func4();  
} 

Curiosamente, la unión de un temporal a un const ref erence es perfectly legal C++.

int main() 
{ 
    // This works! The returned temporary will last as long as the reference exists 
    const big_object& o = func4();  
    // This does *not* work! It's not legal C++ because reference is not const. 
    // big_object& o = func4(); 
} 
+1

Beautiful explanation. : hattip: en el tercer fragmento de código, está eliminando 'int * p = func2(); delete p; 'Ahora, cuando eliminaste 'p', ¿significa que la memoria asignada" dentro "de la función' func2() ' también se eliminó? –

+1

@Anisha Kaul: Sí. La memoria se asignó dentro de 'func2()' y se liberó afuera en la siguiente línea. Sin embargo, es una forma bastante propensa a los errores manejar la memoria, como dije, usarías alguna variante de RAII en su lugar. Por cierto, parece que estás aprendiendo C++. Recomiendo recoger [un buen libro introductorio de C++] (http://stackoverflow.com/questions/388242) para aprender. Además, para referencia futura, si tiene alguna pregunta, siempre puede publicar la pregunta en Desbordamiento de pila. Los comentarios no están destinados a hacer preguntas totalmente nuevas. –

+0

¡Ahora entendí, lo has hecho bien! La función devolvía un puntero, y fuera de esa función, ha borrado la memoria a la que apuntaba. Está claro ahora, y gracias por el enlace. –

13

Una variable local es la memoria en la pila, esa memoria no se anula automáticamente cuando se sale del alcance. Desde una función más anidada (más arriba en la pila en la memoria), es perfectamente seguro acceder a esta memoria.

Una vez que la función vuelve y termina, las cosas se vuelven peligrosas. Normalmente, la memoria no se borra ni se sobrescribe cuando regresa, lo que significa que la memoria en esa dirección aún contiene sus datos: el puntero parece válido.

Hasta que otra función construya la pila y la sobrescriba. Esta es la razón por la cual esto puede funcionar por un tiempo y luego deja de funcionar repentinamente después de que un conjunto de funciones particularmente anidado profundamente, o una función con objetos realmente grandes o muchos locales, llega a esa memoria de pila nuevamente.

Incluso puede suceder que vuelva a llegar a la misma parte del programa y sobrescriba su antigua variable de función local con la nueva variable de función. Todo esto es muy peligroso y debe desalentarse en gran medida. ¡No utilice punteros a objetos locales!

1

Una cosa buena de recordar son estas simples reglas, y se aplican tanto a los parámetros como a los tipos de devolución ...

  • Valor: realiza una copia del elemento en cuestión.
  • Puntero - se refiere a la dirección del artículo en cuestión.
  • Referencia: literalmente es el elemento en cuestión.

Hay un momento y lugar para cada uno, así que asegúrese de conocerlos. Las variables locales, como has mostrado aquí, son solo eso, limitadas al tiempo que están localmente activas en el alcance de la función. En su ejemplo, tener un tipo de devolución de int* y devolver &i habría sido igualmente incorrecto. Usted sería mejor en ese caso hacer esto ...

void func1(int& oValue) 
{ 
    oValue = 1; 
} 

Si lo hace, podría cambiar directamente el valor de su pasado en el parámetro. Mientras que este código ...

void func1(int oValue) 
{ 
    oValue = 1; 
} 

no lo haría. Simplemente cambiaría el valor de oValue local a la llamada de función. La razón de esto es porque en realidad estaría cambiando solo una copia "local" de oValue, y no de oValue.

-3
> int &f(); int x; 
> 
> int main() { 
>  f() = 100; 
>  cout<<x; 
>  return 0; } 
> 
> int &f() { 
>  int x; 
>  return x; } 

el retorno al de una función f es un puntero de tipo int, pero u rentabilidad variable int que no está puntero de modo que por eso se genera el error. use un puntero de tipo entero en la función f.

+1

Devuelve una referencia, no un puntero. La respuesta aceptada es correcta. – SirGuy

Cuestiones relacionadas