C ++ Retornando referência a variável local

O código a seguir (func1 ()) está correto se tiver que retornar i? Lembro-me de ler em algum lugar que há um problema ao retornar a referência a uma variável local. Como é diferente de func2 ()?

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

Este trecho de código:

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

não funcionará porque você está retornando um alias (uma referência) para um object com vida útil limitada ao escopo da chamada de function. Isso significa que uma vez que func1() retorna, int i morre, fazendo com que a referência retornada da function seja inútil, porque agora se refere a um object que não existe.

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

A segunda versão funciona porque a variável é alocada no armazenamento gratuito, que não está vinculada ao tempo de vida da chamada de function. No entanto, você é responsável por delete o int alocado.

 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, você envolveria o ponteiro em alguma class RAII e / ou em uma function de fábrica para não precisar delete lo.

Em ambos os casos, você pode simplesmente retornar o valor em si (embora eu tenha percebido que o exemplo que você forneceu foi provavelmente inventado):

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

Note que é perfeitamente func3() retornar objects grandes da mesma maneira que func3() retorna valores primitivos, porque praticamente todo compilador atualmente implementa alguma forma de otimização de valor de retorno :

 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, ligar um temporário a uma referência const é um C ++ perfeitamente legal .

 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(); } 

Uma variável local é a memory na pilha, essa memory não é automaticamente invalidada quando você sai do escopo. De uma function mais profunda aninhada (maior na pilha na memory), é perfeitamente seguro acessar essa memory.

Quando a function retorna e termina, as coisas ficam perigosas. Normalmente, a memory não é excluída ou sobrescrita quando você retorna, o que significa que a memory nesses endereços ainda contém seus dados – o ponteiro parece válido.

Até que outra function construa a pilha e sobrescreva-a. É por isso que isso pode funcionar por um tempo – e então, de repente, deixar de funcionar depois que um conjunto de funções particularmente profundamente aninhadas, ou uma function com objects realmente grandes ou muitos locais, atingir essa memory de pilha novamente.

Pode até ocorrer que você atinja a mesma parte do programa novamente e sobrescreva sua variável de function local antiga com a nova variável de function. Tudo isso é muito perigoso e deve ser fortemente desencorajado. Não use pointers para objects locais!

É bom lembrar que essas regras são simples e se aplicam a ambos os parâmetros e tipos de retorno …

  • Valor – faz uma cópia do item em questão.
  • Ponteiro – refere-se ao endereço do item em questão.
  • Referência – é literalmente o item em questão.

Há um tempo e um lugar para cada um, portanto, certifique-se de conhecê-los. As variables ​​locais, como você mostrou aqui, são apenas isso, limitadas à hora em que estão localmente vivas no escopo da function. No seu exemplo, ter um tipo de retorno de int* e retornando &i teria sido igualmente incorreto. Você estaria melhor nesse caso fazendo isso …

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

Isso alteraria diretamente o valor do seu parâmetro passado. Considerando que esse código …

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

não faria. Apenas mudaria o valor de oValue local para a chamada de function. A razão para isso é porque você realmente está mudando apenas uma cópia “local” de oValue , e não oValue si.