• ベストアンサー

スコープ外でのアドレスの参照についての疑問

C++を勉強していて解らないことがあったので質問させていただいたのですが、 とあるスコープで生成したインスタンスを、 スコープ外で参照しても問題なく値が取り出せるのですけども、 これは処理的には問題ないのでしょうか? 例のソースを以下に示します。 struct Hoge {  int a, b; }; void testA(){  Hoge *hoge;  if( true ){   Hoge tmp;   tmp.a = 1;   tmp.b = 2;   hoge = &tmp;  }  std::cout << hoge->a << "," << hoge->b << std::endl; } void testB(){  Hoge *pHoge;  if( true ){   Hoge *pTmp = new Hoge();   pTmp->a = 1;   pTmp->b = 2;   pHoge = pTmp;  }  std::cout << pHoge->a << "," << pHoge->b << std::endl;  delete pHoge;  pHoge = NULL; } 上記の場合、どちらも画面に 1,2 と出力されますが、 アドレスが上書きされていないだけで今の時点では偶然エラーが出ないけども潜在的なバグがあったり、 処理的にいつ落ちるかわからないなどの問題はあったりしますか? ご存知の方がいらっしゃいましたらアドバイスをお願いします

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

 こんにちは。御礼頂き有難う御座います。 >>連結リストクラスなるものの内部でインスタンスを生成しアドレスを前後でつなぎ、最終的にデストラクタで全て解放するように考えていたのですが、 >>こういったことはあまり実用的ではないのでしょうか?  いえ、流石に実用的で無いと言う訳では無いのですが、処理の過程でポインタ複写されて重複する事もあり、うっかりすると2重削除が起こったりしやすくなります。  場合にもよりますが、そのままポインタを扱い続けるよりは、参照カウント方式のスマートポインタを扱った方が、手っ取り早くて安全度が高いです。  少なくとも、「newとdeleteが一対か?」と言う呪われたお話に付き合う必要はなくなります。  以下参考を示しますが、極小さなコードなので、中々恩恵が実感できないかもしれません。STLとBOOSTを使用しています。 //必要な物 #include<list> #include<algorithm> #include<boost/shared_ptr.hpp> //削除用のコールバック void Delete(Hoge* p) { cout << "[pointer deleted : " << p << "]" << endl; delete p; } //通常のポインタ int main() { Hoge* a[] = { new Hoge(), new Hoge(), new Hoge() }; std::list<Hoge*> ls(a, a + 3); //Hogeポインタを任意で削除しなければいけない std::for_each(ls.begin(), ls.end(), &::Delete); return 0; } //スマートポインタ int main() { typedef boost::shared_ptr<Hoge> sptr; sptr a[] = { sptr(new Hoge(), &::Delete), sptr(new Hoge(), &::Delete), sptr(new Hoge(), &::Delete) }; std::list<sptr> ls(a, a + 3); //何もする必要は無い。スマートポインタのデストラクタが勝手に動いてHogeポインタを消す。 //また、std::list<sptr> ls2 = ls; の様に複写しても2重削除される事も無い。 return 0; }

hiroko_ss
質問者

お礼

ご返答ありがとうございます。 サンプルソースまで乗せていただいて感謝します。 私自身がスマートポインタ等の理屈を十分に理解できていないのでさっと見た感じではありますが、 この場合だと二重で削除されて強制終了や解放漏れ・・・ なんてことは無さそうなのでより安全ではありますよね。 生成時と解放時に必ず1度だけ呼ばれるのですから。 アドバイスいただいたソースを元に自分なりに色々と手を加えてみたいと思います。 ありがとうございます。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (3)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

testB の方はこれだけみると変だけど, (new/delete の問題を除けば) 特におかしくもないですよ. 「ある関数の中で new で確保し, その領域をそのまま返す」というのはまさにこれの延長線上にあるわけだから. まあ混乱すると簡単に死ねるのでスマートポインタを使うなり GC 使うなりした方がいいと思うけど.

hiroko_ss
質問者

お礼

ご返答ありがとうございます。 言われてみれば確かに、 FactoryMethodパターンなんかはそうですよね。 そうならば納得できます。 うーん、やっぱり普通にnew/deleteを使うのでは、 先の方も仰られたとおり解放し忘れや二重解放がありますし・・・。 このまま使わずに、何かしらの方法を考えたいと思います。

全文を見る
すると、全ての回答が全文表示されます。
回答No.2

 こんばんは。 >>アドレスが上書きされていないだけで今の時点では偶然エラーが出ないけども潜在的なバグがあったり、  TestAに関してはその通りです。  これは明らかにNGです。まだ残骸が残っているのでしょう。完全な間違いである、と言う事です。    TestBはOKですが、厳密には長丁場に置いてこの様な事をしていると・・・。

hiroko_ss
質問者

お礼

確かに長丁場でやっていると、 解放し忘れなどが気になりますよね。 その辺りは十分に注意するようにします・・・。 ただ一応可能とのことなので、 連結リストクラスなるものの内部でインスタンスを生成しアドレスを前後でつなぎ、最終的にデストラクタで全て解放するように考えていたのですが、 こういったことはあまり実用的ではないのでしょうか? ご回答に更に質問で返すようで申し訳ないですが、 ご存じでしたらご返答いただければ大変嬉しいです

全文を見る
すると、全ての回答が全文表示されます。
  • jx-word
  • ベストアンサー率40% (38/94)
回答No.1

testAはたまたま動いているだけですね。 struct Hogeにデストラクタを実装してみると、へんてこな動作をしているのが分かります。

hiroko_ss
質問者

お礼

ご回答ありがとうございます。 testAの場合デストラクタを呼び出すと、 確かにcout以前にデストラクタが出てきて想定外の動きをしてしまいますね・・・。 本当に、たまたま動いているだけなんですね。。 使わないよう十分に注意したいと思います。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 変数の隠蔽とスコープ

    いつまでも初心者のままじゃ駄目だなあと思い, 用事のついでに本屋まで行って 評価が高いらしい http://www.amazon.co.jp/exec/obidos/ASIN/4797328541/Lvdrfree-22?dev-t=D1KDF7Q74DD3A2%26camp=2025%26link_code=xm2 を100ページほど立ち読みしてみた。(買ってません) で、まあ内容を理解できているかは別として 可視性とアクセス制御という話があって Java や C#の時の知識から 以下のコードの出力結果は一応理解できます。 折角なので 質問もコードの中に書いてみました。 ========================== #include <iostream> int a = 5; class fuga{ public: int a; fuga(){ a = 3; } }; class hoge : fuga{ //実際の書籍では public fugaだったような public: void printa(){ std::cout << a << std::endl; //コンパイル通っちゃったけど,ここからグローバル変数a(==5)にアクセスする方法はあるの? } }; int main(){ hoge* p = new hoge; p->printa(); std::cout << a << std::endl; delete p; // 3 // 5 }

  • 条件の配列

    表題の件に付いて質問させていただきます。 複数の条件分岐を簡便に記述する方法を探しております。 簡単な例でいいますと、 int main(){  int value=3;  if(value==0)std::cout <<"value="<<0 <<std::endl;  if(value==1)std::cout <<"value="<<1 <<std::endl;  if(value==2)std::cout <<"value="<<2 <<std::endl;  if(value==3)std::cout <<"value="<<3 <<std::endl; } 上記プログラムの複数の条件分岐をfor文をもちいてかきかえる場合、conditionクラスを作成して class condtion{ private:  int *a;  int b; public  void SetPointer(int* value){a=value}  void SetVal(int val){b=val}  bool isTrue(){   if(*a==b){return true;}   else{ return faulse;}  } } int main(){  int value=3;  condition c[4]; //クラス  for(int i=0;i<4;i++){   c[i]->SetPointer(&value);   c[i]->SetVal(i);  }  for(int i=0; i<4; i++){   if(c[i].isTrue())std::cout<<"i="<<i<<std::endl;  } } と実装できそうな気がするのですが、(上記プログラムは動作確認しておりません。) 変数が増えたり、条件が複雑になると少々繁雑になってしまいそうな気がしております。 もっと簡便に条件分岐を配列として処理する方法は存在しますか? 関数ポインタなどがつかえるのでしょうか? アドバイスよろしくお願いいたします。

  • 添字演算子

    #include <iostream> class hoge{ private: int a; public: hoge(){ a = 0; } int operator+(int fuga){ a = a + fuga; return a; } int operator[](int fuga){ return 1; } }; int main(){ hoge* p; p = new hoge; std::cout << ((*p) + 5) << std::endl; std::cout << ((*p)[1] ) << std::endl; // 5 // 1 // std::cout << ((*p) [] 1); エラーです。何故ですか? }

  • VC++でプログラムの勉強をしています。

    プログラムは最近はじめたばかりです。While文とif文を使ってクイズを作ってみたところ、一個目のsinで入力を求めているところから無限ループになってしまいました。色々調べてcin.cler()とsin.ignore()を入れたりもしてみましたが上手くいきませんでした。どこを間違えているのでしょうか? //クイズ #include <iostream> using namespace std; int main()//cin.clear();cin.ignore();???? { int a; int b; while(1) { cout<<"ネコ型のロボットが出てくるアニメといえば?"<<endl; cout<<"A)ドラえもん B)ドラエもん C)ほりえもん D)サザエさん"<<endl; cin>>a; if(a=='A') { cout<<"ファイナルアンサー?"<<endl; cout<<"Y)Yes N)NO"<<endl; cin>>b; if(b=='Y'){break;} if(b=='N'){cout<<"ゆっくり考えてね!!"<<endl;} if(b!='Y'||'N'){cout<<"正しく入力してね!"<<endl;} } if(a=='B'||'C'||'D') { cout<<"ファイナルアンサー?"<<endl; cout<<"Y)Yes N)NO"<<endl; cin>>b; if(b=='Y'){cout<<"残念!!"<<endl;} if(b=='N'){cout<<"ゆっくり考えてね!!"<<endl;} } if(a!='A'||'B'||'C'||'D'){cout<<"正しく入力してね!"<<endl;} } cout<<"正解!!"<<endl; }

  • C#で参照

    C#で参照 こんにちは。 例えば、C++で class A { int &x; public: A(int &_x):x(_x){} public: void disp() { std::cout<<x<<std::endl; } }; void func() { int x=100; A a(x); a.disp(); x=200; a.disp(); } とすると、a.xとxとを連動させることができますが、同様のことをC#で行うにはどうすればよいのでしょうか? int x=100; の定義を変えずに行うことは可能でしょうか? よろしくお願いします。

  • フレンド宣言のスコープがわかりません。

    フレンド宣言のスコープがわかりません。 #include <iostream> class X { friend class Y; friend void f() { std::cout << "f()" << std::endl; } }; //class Y; //void f(); class Z { Y *ymem; void g() { ::f(); } }; int main() { ::f(); return 0; } C++プライマー第4版を読んでいます。511ページに上記のプログラムがあり、 「フレンドで導入されたクラスと関数(定義または宣言)は、すでに宣言されたものとして使うことができる。」 と説明されています。 しかし、g++(gcc4.4.1)でコンパイルするとエラーになります。コメント部分を外すと実行できます。 C++プライマーが正しいのか、gccが正しいのか、私が思い違いをしているのか、わかりません。 ご存知の方はいませんか。

  • switch文のエラーについて

    次のプログラムを実行したら slect.cpp:In member function `void select::setBlood(std::string)': slect.cpp:16:error: switch quantity not an integer が出ました。switch文の何が不完全ですか?教えてください。 #include<iostream> #include<string> using namespace std; class select { private: string blood; public: void setBlood(string b); void show(); }; void select::setBlood(string b){ blood=b; switch(b){ case 'A': cout<<"A is best"<<endl; break; case 'B': cout<<"B is best"<<endl; break; case 'AB': cout<<"AB is best"<<endl; break; case 'O': cout<<"O is best"<<endl; break; default: cout<<"crazy"<<endl; break; } } void select::show(){ cout<<"Your blood type is :"<<blood<<endl; } int main(){ string b; select Q; cout<<"Please Input your Blood type in A or B or AB or O:"<<endl; cin>>b; Q.setBlood(b); Q.show(); return 0; }

  • (C++)スマートポインタをメンバ変数で使いたい

    Viual Studio 2013を使ってC++のコードを書いています。 以下のコードで、new-deleteの クラス生成をスマートポインタで置き換えたいのですが、 うまくいきません。具体的には/* not smart */の部分を スマートポインタで置き換えたいが、C2059のエラーが出てきて コンパイルできないのが理解できていません。 どなたかわかる方教えていただければ幸いです。 よろしくお願いします。 // ----コードは以下---- #include <memory> #include <iostream> class Hoge { public: Hoge(){ std::cout << "constructed!" << std::endl; }; ~Hoge(){ std::cout << "destructed!" << std::endl; }; private: }; class Hogehoge { public: Hogehoge(){}; ~Hogehoge(){ delete test; /* not smart */}; private: // error C2059 w/ VS2013 // std::unique_ptr<Hoge> test(new Hoge); Hoge* test = new Hoge; /* not smart */ }; void main() { Hogehoge foo; }

  • 関数を順番に呼び出したい

    一度、ここで質問をしたのですが、わたくしの勘違いで質問締め切ってしましました。 再度の投稿をお許しください。 関数test()の中で、関数testA 、関数testBの順番で呼び出したいと思っています。 関数testAで、return true;を入て、if文でtestAがtrueだったらtestBを呼ぶという処理を行う場合、どこにreturn testを入れたらよいのか迷っています。 またtestAの最後にtestBを入れる場合は、submit()の前も後もどちらに書いても呼ばれないですよね。 ちなみに、testA,testBは以下のような感じです。 testA(){ action="××update××"; method="post"; target="FRAMEH"; submit(); } testB(){ action="××insert××"; method="post"; target="FRAMEH"; submit(); }

  • Java→C++:オブジェクトの扱いに戸惑ってます

    C++を学び始めた者です。 これまでJavaを中心に開発していました。 オブジェクトの扱い方に関して若干戸惑っております。 下記のようなコードを実行したところ、問題なく動くことを確認しました。 しかし、Javaでは型の宣言はあくまで型を宣言しただけだったのに対し、 C++では宣言を行っただけでコンストラクタが動き、メンバ変数まで使えます。 下記のコードが動き実行結果も下記のようになることからその様に考えました。 これはJavaでいうところの型宣言の時点でC++はオブジェクトが作成されており、 実態が有ると考えて良いのでしょうか? しかし、new演算子はc++にも存在しているはずです。 上記の事を踏まえた上で Hoge hoge; とするのと  Hoge hoge = new Hoge(); とすることの違いを教えて下さい。 何か基本的なところで考えが誤っているのでしょうか? ----------------------------------------- #include "stdafx.h" #include <iostream> using namespace std; class Hoge { public: int hogeValue; Hoge():hogeValue(1000) { std::cout << "Hoge was constructed." << std::endl; PrintMessage(); getchar(); } public: void PrintMessage(){ std::cout << "PrintMessage was constructed. hogeValue = "<<hogeValue << std::endl; getchar(); } }; int main() { std::cout << "main() was called." << std::endl; getchar(); Hoge hoge; hoge.hogeValue=2000; hoge.PrintMessage(); } ---------------------------------------- 実行結果 main() was called. Hoge was constructed. PrintMessage was constructed. hogeValue = 1000 PrintMessage was constructed. hogeValue = 2000