2011-01-10 19 views
80

iを返す必要がある場合、次のコード(func1())は正しいですか?私は、ローカル変数への参照を返すときに問題があるとどこか読んでいることを覚えています。 func2()との違いは?C++ローカル変数への参照の返却

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

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

正しくありません。 –

+1

func1()を動的に割り当てられたメモリを使用するように変更した場合、それらは同じです:-) 'int&i = *新しいint;' –

+1

@Martin誰がそれを削除しますか? – balki

答えて

142

このコードスニペット:あなたは関数呼び出しの範囲には限定寿命を持つオブジェクトへのエイリアス(参照)を返すしているので

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

が動作しません。つまり、func1()が返されると、int iが死んでしまい、存在しないオブジェクトを参照するため、参照が関数から無駄に返されます。変数は、関数呼び出しの有効期間にバインドされていない空き店舗、上に配置されているので

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

番目のバージョンは、作業を行います。ただし、deleteは割り当て済みintです。あなたはそれを自分deleteする必要はありませんので、

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 
} 

通常は、いくつかのRAIIクラスおよび/またはファクトリ関数にポインタをラップします。いずれの場合も

(私はあなたがおそらく不自然だった例を実現するが)、あなただけの値自体を返すことができます。

int func3() 
{ 
    return 1; 
} 

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

注それは大きなオブジェクトを返すためにfunc3()戻っプリミティブと同じよう完全に罰金だということは、値のほぼすべてのコンパイラは、最近return value optimizationのいくつかのフォームを実装するので:のconst refに一時的に結合、興味深いことに

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

を例は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

美しい説明。 :hattip:3番目のコードスニペットで、あなたは 'int * p = func2();を削除しています。 delete p; '今度は、 'p'を削除したときに、関数func2()の中に割り当てられたメモリ の定義も削除されたことを意味しますか? –

+1

@アニシャ・カール:はい。メモリは 'func2()'の内部に割り当てられ、次の行に外部から解放されました。 RAIIの変種を代わりに使用すると言ったように、メモリを扱うのはむしろエラーを起こす傾向があります。ところで、C++を学んでいるように聞こえます。私は[良い入門C + +の本](http://stackoverflow.com/questions/388242)から学ぶことをお勧めします。また、将来の参照のために質問がある場合は、常にStack Overflowに質問を投稿することができます。コメントは、全く新しい質問をすることを目的としたものではありません。 –

+0

今、私はあなたがそれを正しくしたことを理解しました!この関数はポインターを返していて、その関数の外にポインターを指していたメモリーを削除しました。今はっきりしていて、リンクに感謝しています。 –

13

ローカル変数はスタック上のメモリで、スコープ外に出るとそのメモリは自動的に無効化されません。より深くネストされた関数(メモリ内のスタックの上位)から、このメモリに完全に安全にアクセスできます。

機能が戻って終了すると、物事が危険に陥ります。 通常、返されたときにメモリは削除または上書きされません。そのアドレスのメモリにはまだデータが格納されていることを意味します。

別の関数がスタックを構築して上書きするまでは、 これはしばらくの間動作し、特に深くネストされた関数のセット、または本当に大きなサイズのオブジェクトや多くのローカルオブジェクトを持つ関数がそのスタックメモリに再び到達した後、突然機能しなくなる理由です。

同じプログラム部分に再度アクセスし、古いローカル関数変数を新しい関数変数で上書きすることさえあります。すべてこれは非常に危険であり、重大に落胆する必要があります。 ローカルオブジェクトへのポインタを使用しないでください!

1

これらの単純なルールは覚えておくと便利ですが、パラメータと戻り値の型の両方に適用されます...

  • 値 - 問題のアイテムのコピーを作成します。
  • ポインタ - 問題の項目のアドレスを指します。
  • 参照 - 文字通り問題のアイテムです。

それぞれ時間と場所がありますので、それらを知るようにしてください。ローカル変数は、ここに示したように、関数スコープ内でローカルに存続する時間に限られています。戻り値のタイプがint*で、&iを返すという例では、同じように間違っています。その場合、これを行う方が良いでしょう...

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

これを行うと、渡されたパラメータの値が直接変更されます。一方、このコードは...

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

はありません。それは関数呼び出しのローカルでoValueの値を変更するだけです。これは、実際にoValueのローカルコピーであり、oValueではなく、それだけで変更するためです。

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

関数fの戻り値はint型のポインタですが、ポインタ変数でないint型の変数を返すので、エラーが発生します。 f関数の整数型のポインタを使用します。

+1

ポインタではなく参照を返します。受け入れられた答えは正しいです。 – SirGuy

関連する問題