- ベストアンサー
C++のnew演算子の挙動について
- C++のnew演算子を使用した際に、予想と異なる挙動が起きることがあります。
- 関数にポインタを渡して値を得ようしたプログラムでは、ポインタの値が正しく設定されず、異常な値や例外が発生することがあります。
- この問題を回避するためには、ポインタをポインタへのポインタとして渡し、そのポインタを使ってnew演算子でメモリを確保する必要があります。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
たとえば、 void test(int a) { a = 100; } では値が変わらないことは理解できますよね。 void test(int* a) { *a = 100; } なら値が変えられることも理解できているでしょう。 int*という型を仮にintptrに置き換えると、質問の最初のtestは void test(intptr a) { a = new int(100); } となり、うまくいく方のtestは void test(intptr* a) { *a = new int(100); } となります。違いがわかりますか? ポインタも変数ですから、ポインタの値だけ変えても関数の外には影響しません。 関数内で内容を変更したいのであれば、その変数のポインタを渡して、関数内ではポインタが指す内容を変更しなければなりません。 intのポインタを変更したいのであれば、intのポインタのポインタを渡して、関数内ではintのポインタ のポインタが指す内容(=intのポインタ)を変更します。
その他の回答 (4)
- Tacosan
- ベストアンサー率23% (3656/15482)
「戻り値はエラー処理の関係でブーリアン固定」というのはどういうことでしょうか? 挙げられたプログラムはそうなっていませんよね. 「どんな関数でも戻り値はブーリアンにしなければならない」とかいうコーディング規約だったら.... まあ, しょうがないから従いますね, 「こんな規約作ったやつはきっとばか」と思いながら.
お礼
いえいえ、大した理由ではありませんよ。 WIN32APIは基本的に戻り値にエラー情報、引数に受け取るポインタという関数が多く存在しますから、互換性を高めたりやら、ネスト構造を組む際に状態を管理しやすいとか、そんな単純な理由です。 繰り返しますが、大した理由ではないです、はい。 ご指摘、ありがとうございました。
- CanvasShoes
- ベストアンサー率64% (16/25)
値渡し、ポインタ渡し、参照渡しの違いをよく味わってみてください。 1番目のプログラムは、次のようにすれば動作するはずです。 【案1】 void test( int* &refpA ) { refpA = new int( 100 ); } 【案2】 int *test(void) { return new int( 100 ); } 呼び出し側を、 b = test() ; とする。 もちろん、ご自身で解決された 【案3】 void test( int** ppA ) { *ppA = new int( 100 ); } も正解です。 上記のコードで、変数名の先頭の「p」はポインタ。「pp」はポインタのポインタ。「ref」は参照であることを示しています。 このようにすると、参照先の型が理解しやすくなるので、個人的には型のわかる変数名つけることをお勧めします。
お礼
ご回答、感謝です。 私は見やすさ(メリハリ?)重視でコーディングするので、エイリアスはまず使いませんが、ポインタと組み合わせることもできたんですね。 ですが、両方使うとやはり小難しいイメージが拭いきれませんね。 変数名もref,p,ppといった共通項目は大切ですよね。 私もポインタには頭にlp(LP)を付与しますがポインタのポインタのケースだとlplp###となって見苦しいですね…。 戻り値はエラー処理の関係でブーリアン固定なので、1番最初に消去した方法でしたが、この方法も有効なんですね。 数々のサンプル、参考にさせて頂きました。ありがとうございました。
- D-Matsu
- ベストアンサー率45% (1080/2394)
void test(int a) { a = 100; } int main(void) { int b; test(b); printf("%d\n", b); return 0; } とやってもbの値が書き変わらないのと同じ理由です。 「納めたいものの型へのポインタ」が必要。 #まぁ参照使ってもいい訳ですがね
お礼
ご回答、感謝です。 >「納めたいものの型へのポインタ」が必要 今回のケースでは、ポインタに収めたいからポインタのポインタを使うということですね。 ポインタを1つの変数と考えることで合点がいきますね。 ありがとうございました。
- magicalpass
- ベストアンサー率58% (378/648)
> void test( int* a ) > { > a = new int( 100 ); > } a 自体は引数に与えられたint型変数のアドレス値を持つローカル変数なので、直接ここに何を代入してもreturn等で返さない限りは呼び元に結果が返りません。したがって 呼び元の b は初期化されないままのゴミが入ってるだけなのでそれをアドレスとして扱ったりするとエラーになるに決まってます。 > void test( int** a ) > { > *a = new int( 100 ); > } こちらはint型のアドレス変数のアドレス値が a に渡されているので、そのアドレス先にnewの結果を代入することで呼び元に結果を返しています。 > new演算子は自動的には破棄されないのではないのでしょうか? C++ではC#のような自動的なガベージコレクションは備わっていないので、newで獲得されたメモリは明示的にdeleteする必要があります。
お礼
ご回答、感謝です。 むむ、引数のポインタもローカル変数として扱われるんですね。ポインタ=アドレスと考えていましたので(実際に無理矢理、直接値を渡す事も可能なので)ローカル変数として扱われるとは考え難いことですね…。 new演算子で確保した領域も無事(?)メモリリークすることが分かりました。ありがとうございました。
お礼
ご回答、感謝です。 なるほどたしかにポインタをtypededすればポインタも1つの変数であるということがよく分かりますね。 非常に理解しやすい解説、ありがとうございました。