次のコード(func1())は、iを返す必要がある場合、正しいですか?ローカル変数への参照を返すときに問題があることをどこかで読んだことを覚えています。func2()とどう違うのですか?
int& func1()
{
int i;
i = 1;
return i;
}
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
次のコード(func1())は、iを返す必要がある場合、正しいですか?ローカル変数への参照を返すときに問題があることをどこかで読んだことを覚えています。func2()とどう違うのですか?
int& func1()
{
int i;
i = 1;
return i;
}
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
回答:
このコードスニペット:
int& func1()
{
int i;
i = 1;
return i;
}
関数呼び出しのスコープに制限された存続期間を持つオブジェクトにエイリアス(参照)を返すため、機能しません。これは、一度func1()戻るとint i死に、存在しないオブジェクトを参照するため、関数から返された参照が無意味になることを意味します。
int main()
{
int& p = func1();
/* p is garbage */
}
2番目のバージョンは、変数がフリーストアに割り当てられ、関数呼び出しの存続期間にバインドされていないため、機能します。ただし、delete割り当てられたを使用する必要がありますint。
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クラスやファクトリ関数でラップするのでdelete、自分でそれを行う必要はありません。
どちらの場合でも、値自体を返すことができます(提供した例はおそらく不自然なものであると思いますが)。
int func3()
{
return 1;
}
int main()
{
int v = func3();
// do whatever you want with the returned value
}
func3()現在ほとんどすべてのコンパイラーが何らかの形の戻り値の最適化を実装しているため、プリミティブ値を返すのと同じ方法で大きなオブジェクトを返すことは完全に問題ありません。
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();
}
興味深いことに、テンポラリをconst参照にバインドすることは完全に正当な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();
}
int* p = func2(); delete p;ます。「p」を削除すると、関数func2()の定義の「内部」に割り当てられたメモリも削除されたということですか。
ローカル変数はスタック上のメモリであり、スコープ外に出てもそのメモリは自動的に無効にはなりません。深くネストされた関数(メモリのスタックの上位)から、このメモリにアクセスすることは完全に安全です。
関数が戻って終了すると、事態は危険になります。通常、メモリは戻ったときに削除も上書きもされません。つまり、そのアドレスのメモリにはまだデータが含まれています。ポインタは有効なようです。
別の関数がスタックを構築して上書きするまで。これがしばらくの間機能する理由です-そして、特に深くネストされた関数のセット、または本当に巨大なサイズまたは多くのローカルオブジェクトを持つ関数が再びそのスタックメモリに到達した後、突然機能を停止します。
同じプログラム部分に再び到達し、古いローカル関数変数を新しい関数変数で上書きすることも起こり得ます。これはすべて非常に危険であり、あまりお勧めできません。 ローカルオブジェクトへのポインタは使用しないでください。
覚えておくと良いのは、これらの単純なルールであり、それらはパラメーターと戻り値の型の両方に適用されます...
それぞれに時間と場所があるので、それらを知るようにしてください。ここで示したように、ローカル変数はそれだけであり、関数スコープ内でローカルに生きている時間に制限されます。あなたの例では、戻り値の型int*と戻り値&iが同じように正しくないでしょう。あなたはその場合これを行う方が良いでしょう...
void func1(int& oValue)
{
oValue = 1;
}
そうすると、渡されたパラメータの値が直接変更されます。一方、このコードは...
void func1(int oValue)
{
oValue = 1;
}
しません。oValue関数呼び出しのローカルの値を変更するだけです。これは、実際にはの「ローカル」コピーだけを変更するためoValueであり、oValueそれ自体を変更することはないためです。
int& i = * new int;