存在するはずのインスタンスを見失う

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
迷彩吹雪

存在するはずのインスタンスを見失う

#1

投稿記事 by 迷彩吹雪 » 15年前

初めて質問させて頂きます。
C++とDXライブラリでゲームを作っているのですが、プログラム実行中にエラーが発生し、
強制終了する現象に悩まされています。

エラーの発生個所を追跡したところ、あるクラスがそのメンバ変数に保持した別のインスタンスのアドレスを、
プログラムの開始直後に見失っている事がわかりました。
問題のクラス類を簡単に示すと以下のようになります。
//----------------------------------------------------------------------------//
//ヘッダファイル////////////////////////////////////////////////////////////////

const int TEISU_A = 16;    //作るクラスAインスタンスの数
const int TEISU_B = 4;    //作るクラスBインスタンスの数

class A{
private:
    //データ類
}

class B{
private:
    A* mB_A;    //クラスAインスタンスのアドレスを保持するポインタ
    //その他のデータ類
public:
    void SetA(A*);    //mB_Aにアドレスの値を代入する関数
}

class I{    //シングルトンクラス
private:
    static I* mInstance;    //自身を指すポインタ
    I();
    ~I();
    
    A* mI_A[TEISU_A];    //クラスAのアドレスを保持するポインタ
    B* mI_B[TEISU_A];    //クラスBのアドレスを保持するポインタ
public:
    static void Create();    //シングルトン生成関数
    static void Destroy();    //シングルトン破棄関数
    static I* Instance();    //クラスIを利用するにはまずこれを呼ぶ必要がある

    //その他の処理関数
}

//ソースファイル////////////////////////////////////////////////////////////////
//クラスAについての定義
A::A(){}
A::~A(){}

//クラスBについての定義
B::B(){
    mB_A = 0;    //設定しないままだとアクセスした瞬間落ちる
}
B::~B(){};
void B::SetA(A* a){
    mB_A = a;
}

//クラスIについての定義
I::mInstance = 0;    //これをしないとコンパイルが通らない

I::I(){
    for(int i=0; i<TEISU_B; i++){    //クラスBインスタンスを指定の数だけ生成
        mI_B = new B();
    }
    for(int i=0; i<TEISU_A; i++){    //クラスAインスタンスを指定の数だけ生成
        mI_A = new A();
    }
    //クラスAとクラスBの関連付け
    //クラスBのメンバ変数mB_AにクラスAインスタンスのアドレスを代入
    mI_B[0]->SetA(mI_A[0]);
    mI_B[1]->SetA(mI_A[1]);
    mI_B[2]->SetA(mI_A[2]);
    mI_B[3]->SetA(mI_A[3]);
}

I::~I(){
    for(int i=0; i<TEISU_B; i++){{    //クラスBインスタンスを破棄
        delete mI_B;
        mI_B = 0;
    }
    for(int i=0; i<TEISU_A; i++){    //クラスAインスタンスを破棄
        delete mI_A;
        mI_A = 0;
    }
}

void I::Create(){
    mInstance = new I();
}

void I::Destroy(){
    mInstance->~I();
}

I* I::Instance(){
    if(mInstance==0){//初回アクセス時に始めてインスタンス生成
        I::Create();
    }
    return mInstance;/*****ここで問題発生*****/
}

//以下その他の処理関数についての定義...
//----------------------------------------------------------------------------//


クラスIはシングルトンです。
クラスIのコンストラクタが終了するときには全てのメンバ変数に何らかの値が入っています。
しかしメインループ内で最初にI::Instance()を呼び出した時,
I::mInstanceが返される瞬間にそのメンバ変数mI_A[0]だけがインスタンスを見失います。
(デバッグ時に変数を見ると,データ値が「???」になります。)
それ以外の,例えばI::mI_A[1]などには同様の現象は起こっていません。
また,I::mI_B[0]のクラスBインスタンスはそのメンバ変数B::mB_AにI::mI_A[0]と同じアドレスを格納しますが,こちらは見失っていません。よってI::mI_B[0]を通してのアクセスは可能です。
直接I::mI_A[0]にアクセスする処理を実行するとその瞬間に停止します。

そもそもなぜこのような設計になっているのかというと,プログラム中で自由にクラスBとクラスAの関連付けを変更できるようにしたかったからです。
またクラスIはプログラムの各所で呼び出される処理を担っているので,シングルトンとなっています。

なぜmI_A[0]だけがインスタンスを見失ってしまうのでしょうか。
また、どのようにすれば良いのでしょうか。
御指南のほどよろしくお願いします。

OS:WindowsXP SP3
開発環境:VC++EE2008,DXライブラリ
プログラミング歴:半年程度。趣味の範囲。

Poco

Re:存在するはずのインスタンスを見失う

#2

投稿記事 by Poco » 15年前

状況が再現できるmain関数を提供してください。

迷彩吹雪

Re:存在するはずのインスタンスを見失う

#3

投稿記事 by 迷彩吹雪 » 15年前

>ぽこ様

情報不足で申し訳ありませんでした。
プロジェクトから必要なデータだけを抽出したものを下記の場所に用意しました。
よろしくお願いします。

ttp//www1.axfc.net/uploader/Li/so/68011
pass:meisai

実は別プロジェクトとして質問文のコードを元に再現可能なコードを書こうとしたら、簡略化したせいかバグが取れてしまいました。
もしかすると質問文のコードは、見当違いの場所を表した無意味なものだったかも知れません。

Poco

Re:存在するはずのインスタンスを見失う

#4

投稿記事 by Poco » 15年前

★印の処で変更されています。
パッと見、変数mOldInputは要素が1個しか無い(つまりmOldInput[0]までが有効範囲)のに、
mOldInput[1]にアクセスしています。
このmOldInput[1]に該当する部分がmBotton[0]なのでここだけ値が変わったように見えるんです。

----
//毎回呼ぶ関数 入力状態を更新
void Input::getInput(){
for(int i = 1; i <= PadNum; i++){//0番は無視(使ってないので)
mOldInput = mInput; //前回の入力を保存★★★★★
if(i != 1){
mInput = GetJoypadInputState(i+1);//新しい入力を取得
}
else{ //1番はPAD1とキーの二種類の情報なので別処理
mInput = GetJoypadInputState(DX_INPUT_KEY_PAD1);
}
}
}

Poco

Re:存在するはずのインスタンスを見失う

#5

投稿記事 by Poco » 15年前

ついでにデバッグの仕方をば。
VC++のデバッガには、迷彩吹雪さんもご存知の通りブレイクポイントという機能があります。
このブレイクポイントの機能は、
「このファイルのどの行を実行したとき、プログラムを一時停止させる」
機能だけでなく、
「このアドレスに格納されている値が変わったとき、プログラムを一時停止させる」
といった機能もあります。

今回はこれを使用しました。

具体的には、
 ・mBotton[0]が配置されているアドレスに対して、ブレイクポイントを張る。
  メニューからブレークポイントを新規作成したときに「データブレイクポイント」を選択します。
 ・プログラムを実行、上記★印の次のif文で止まった。
 ・変数iが1であることを確認。
 ・変数mOldInputのアドレスを確認
 ・変数mOldInput[1]のアドレスがmBotton[0]と同じであることを確認。
 ・配列の範囲外アクセスと断定。
という感じでデバッグしました。

所要時間は問題の再現確認含めて5分程度でした。
予期しないデータ破壊を見つけるのに便利です。

ご参考までに。 画像

迷彩吹雪

Re:存在するはずのインスタンスを見失う

#6

投稿記事 by 迷彩吹雪 » 15年前

>ぽこ様

素早いご指摘と心を読んだかのような情報提供ありがとうございます。
問題の箇所はまだ配列が0番から始まることに慣れていない時期に書いたものでした。
早速修正し、期待通りの動作を確認しました。
デバッガの使い方ももっと勉強します。
ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る