C ++ Restituzione del riferimento alla variabile locale

Il seguente codice (func1 ()) è corretto se deve restituire i? Ricordo di aver letto da qualche parte che c’è un problema nel restituire il riferimento a una variabile locale. In cosa differisce da func2 ()?

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

Questo frammento di codice:

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

non funzionerà perché stai restituendo un alias (un riferimento) a un object con una durata limitata all’ambito della chiamata alla funzione. Ciò significa che una volta func1() restituisce, int i dies, rendendo inutilizzabile il riferimento restituito dalla funzione perché ora fa riferimento a un object che non esiste.

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

La seconda versione funziona perché la variabile è allocata nell’archivio gratuito, che non è vincolato alla durata della chiamata di funzione. Tuttavia, sei responsabile della delete dell’int allocata.

 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 } 

In genere si avvolgere il puntatore in alcune classi RAII e / o una funzione di fabbrica in modo da non dover delete da soli.

In entrambi i casi, puoi semplicemente restituire il valore stesso (anche se mi rendo conto che l’esempio che hai fornito era probabilmente inventato):

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

Si noti che è perfettamente utile restituire oggetti grandi nello stesso modo in cui func3() restituisce valori primitivi perché quasi ogni compilatore oggigiorno implementa una qualche forma di ottimizzazione del valore di ritorno :

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

È interessante notare che bind un riferimento temporaneo a un const è perfettamente legale 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(); } 

Una variabile locale è la memoria nello stack, quella memoria non viene automaticamente invalidata quando si esce dall’ambito. Da una funzione più profonda annidata (più in alto sulla pila in memoria), è perfettamente sicuro accedere a questa memoria.

Una volta che la funzione ritorna e finisce, le cose diventano pericolose. Di solito la memoria non viene cancellata o sovrascritta quando si ritorna, il che significa che la memoria a quell’indirizzo contiene ancora i propri dati – il puntatore sembra valido.

Fino a quando un’altra funzione crea lo stack e lo sovrascrive. Questo è il motivo per cui questo può funzionare per un po ‘- e poi improvvisamente cessare di funzionare dopo un set di funzioni particolarmente profondamente annidato, o una funzione con dimensioni veramente grandi o molti oggetti locali, raggiunge di nuovo quella memoria stack.

Può anche succedere che si raggiunga di nuovo la stessa parte del programma e si sovrascriva la vecchia variabile di funzione locale con la nuova variabile di funzione. Tutto ciò è molto pericoloso e dovrebbe essere fortemente scoraggiato. Non utilizzare i puntatori agli oggetti locali!

Una buona cosa da ricordare sono queste semplici regole, che si applicano sia ai parametri che ai tipi di ritorno …

  • Valore: crea una copia dell’articolo in questione.
  • Puntatore: si riferisce all’indirizzo dell’elemento in questione.
  • Riferimento – è letteralmente l’object in questione.

C’è un tempo e un posto per ciascuno, quindi assicurati di conoscerli. Le variabili locali, come hai mostrato qui, sono proprio così, limitate al tempo in cui sono localmente attive nell’ambito della funzione. Nel tuo esempio avente un tipo di ritorno di int* e di ritorno &i sarei stato ugualmente scorretto. In questo caso staresti meglio …

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

Fare ciò cambierebbe direttamente il valore del parametro passato. Mentre questo codice …

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

non lo farei. oValue semplicemente il valore di oValue local alla chiamata di funzione. La ragione di ciò è perché in realtà cambierebbe solo una copia “locale” di oValue , e non di oValue stesso.