電子書籍の厳選無料作品が豊富!

C++を勉強していて解らないことがあったので質問させていただいたのですが、
とあるスコープで生成したインスタンスを、
スコープ外で参照しても問題なく値が取り出せるのですけども、
これは処理的には問題ないのでしょうか?
例のソースを以下に示します。

struct Hoge {
 inta, b;
};

void testA(){
 Hoge*hoge;
 if( true ){
  Hogetmp;
  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 と出力されますが、
アドレスが上書きされていないだけで今の時点では偶然エラーが出ないけども潜在的なバグがあったり、
処理的にいつ落ちるかわからないなどの問題はあったりしますか?

ご存知の方がいらっしゃいましたらアドバイスをお願いします

A 回答 (4件)

 こんにちは。

御礼頂き有難う御座います。

>>連結リストクラスなるものの内部でインスタンスを生成しアドレスを前後でつなぎ、最終的にデストラクタで全て解放するように考えていたのですが、
>>こういったことはあまり実用的ではないのでしょうか?
 いえ、流石に実用的で無いと言う訳では無いのですが、処理の過程でポインタ複写されて重複する事もあり、うっかりすると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;
}
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
サンプルソースまで乗せていただいて感謝します。

私自身がスマートポインタ等の理屈を十分に理解できていないのでさっと見た感じではありますが、
この場合だと二重で削除されて強制終了や解放漏れ・・・
なんてことは無さそうなのでより安全ではありますよね。
生成時と解放時に必ず1度だけ呼ばれるのですから。

アドバイスいただいたソースを元に自分なりに色々と手を加えてみたいと思います。
ありがとうございます。

お礼日時:2008/10/09 13:36

testB の方はこれだけみると変だけど, (new/delete の問題を除けば) 特におかしくもないですよ.


「ある関数の中で new で確保し, その領域をそのまま返す」というのはまさにこれの延長線上にあるわけだから. まあ混乱すると簡単に死ねるのでスマートポインタを使うなり GC 使うなりした方がいいと思うけど.
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
言われてみれば確かに、
FactoryMethodパターンなんかはそうですよね。
そうならば納得できます。

うーん、やっぱり普通にnew/deleteを使うのでは、
先の方も仰られたとおり解放し忘れや二重解放がありますし・・・。
このまま使わずに、何かしらの方法を考えたいと思います。

お礼日時:2008/10/09 13:40

 こんばんは。



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

確かに長丁場でやっていると、
解放し忘れなどが気になりますよね。
その辺りは十分に注意するようにします・・・。

ただ一応可能とのことなので、
連結リストクラスなるものの内部でインスタンスを生成しアドレスを前後でつなぎ、最終的にデストラクタで全て解放するように考えていたのですが、
こういったことはあまり実用的ではないのでしょうか?

ご回答に更に質問で返すようで申し訳ないですが、
ご存じでしたらご返答いただければ大変嬉しいです

お礼日時:2008/10/09 10:02

testAはたまたま動いているだけですね。


struct Hogeにデストラクタを実装してみると、へんてこな動作をしているのが分かります。
    • good
    • 0
この回答へのお礼

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

お礼日時:2008/10/09 10:00

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!