「敵をメンバに持つ敵」タスクシステムのdeleteタイミング

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

「敵をメンバに持つ敵」タスクシステムのdeleteタイミング

#1

投稿記事 by wiruka » 9年前

現在 http://codezine.jp/article/detail/297 のページを参考にしてシューティングゲームにタスクシステムを実装しているのですが、
「敵をポインタで操作する敵」、例えばビットを周りに浮かべている敵を作ろうとしていて躓いています。

ゲーム中に出現するオブジェクトをすべてTaskから派生させて
Task内でnew演算子をオーバーロードして new(0.5f) Object(); のように使って
オブジェクトをnewで作るときに自動的にタスクリストにnewの後の括弧内(0.5f)の数字で若い順に組み込まれるようになっています。
(deleteもオーバーロードしてdeleteで開放すると自動的にリストのポインタがつながるようになっています)

そして、以下のようなコードで「敵をポインタで操作する敵」を作ろうとしているのですが
どうもうまくいきません。具体的になんと言えばいいか、まったく問題なく動くときもあれば、いきなり強制終了するときもあるのです。

コード:

//部分部分で省略しています
//teki.h

class Enemy : public TaskEx
{
protected:
public:
	Enemy( float x, float y );	//コンストラクタ
	virtual ~Enemy();	//デストラクタ
};


class Teki_option : public Enemy
{
protected:
public:
	Teki_option( float x, float y );	//コンストラクタ
	~Teki_option();	//デストラクタ

	void run(void);
	void draw(void);

	void xy_set( float x, float y );	//外部からxy軸セット
};

class Teki : public Enemy
{
protected:
	Teki_option *option[5];	//オプション
public:
	Teki( float x, float y );	//コンストラクタ
	~Teki();	//デストラクタ

	void run(void);
	void draw(void);
};

//teki.cpp

Enemy::Enemy( float x, float y )
{
	m_cx = x;
	m_cy = y;
}

Teki_option::Teki_option( float x, float y ) : Enemy( x, y )
{
              //いろいろ初期化
}

void Teki_option::run()
{
	//いろいろ処理実行
}

void Teki_option::draw()
{
	//描画処理
}



Teki::Teki( float x, float y ) : Enemy( x, y )
{
	int i;
	for ( i = 0; i < 4; ++ i )			
		option[i] = new(0.3f) Teki_05_option( 160 - 30 + i * 20, 120 );			//オプションの生成
}

Teki::~Teki()
{
	int i;
	for ( i = 0; i < 4; ++ i )
		if ( option[i] != NULL ) option[i]->die();                   //オプションの死亡フラグを立てる
 }

void Teki::run()
{
	int i;	
	for ( i = 0; i < 4; ++ i )
	{
		if ( option[i] != NULL )
		{
			if ( option[i]->death == TRUE)
				option[i] = NULL;
			else
				option[i]->set( m_log_x[1] - 30 + i * 20, m_log_y[1] + 40 );
		}
	}
}

void Teki::draw()
{
            //描画処理
}


このTekiクラスを new(0.5f) Teki( x, y ); というふうに生成してタスクリストに組み込み、

全タスクリスト->run();
全タスクリスト->draw();
死亡フラグがたっているタスクを delete;

という順番で処理すれば、Tekiクラス側はメンバのTeki_optionクラスがdeleteされる前にリンクを切れるので
思い通りに実行できると思うのですが前述のとおり上手くいくときもあれば強制終了するときもあります。
このコードのどこかに間違いがあるのだと思うのですが自分ではわかりません(´・ω・`)
どなたか何かお気づきでしたらご教授お願いいたします。

アバター
Justy
副管理人
記事: 122
登録日時: 10年前
住所: 神奈川県

Re: 「敵をメンバに持つ敵」タスクシステムのdeleteタイミング

#2

投稿記事 by Justy » 9年前

 終了時のエラーの内容とか、どの程度オリジナルから改変したのか等不明点も多いので
それが原因とは言い切れないのですが。

 何年か前にそのサイトの記事を読んだ時点では、あのサイトのタスクは
他のタスクのポインタを直接保持してはいけない、という仕様だったような
覚えがあります。

 今も変わっていなければ、サンプルのコードもタスクが他のタスクポインタを
保持しないような設計になっているかと思いますので、一度確認してみてはどうでしょうか。

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

Re: 「敵をメンバに持つ敵」タスクシステムのdeleteタイミング

#3

投稿記事 by ISLe » 9年前

Justyさんのおっしゃるように、リンク先の記事で言うところのデフラグをすると、Tekiクラスのメンバoptionで保持しているポインタが無効になってしまいます。
Tekiクラスのデストラクタでオプションの死亡フラグを立てているだけですから、解放処理がワンテンポずれます。
とりあえず解放処理をずれないようにすれば異常終了はふせげるのではないでしょうかね。

タスクが他のタスクのポインタを保持してはいけないだけでなくすべてのタスクのポインタが無効になる可能性を持っています。
こういうのはスマートポインタで関連付けて、最後はタスクが必ず自分でdeleteするように統一しないとバグの温床になります。
スマートポインタを使えば自分でdeleteする処理もタスクシステムに含めて隠蔽化できます。
delete thisしたあと何も実行されないように注意するといった必要もなく、真に必要な処理を書くだけで済ませられます。

リンク先の記事は、独自に工夫されているところがぜんぶ裏目に出て使いにくさや速度低下に繋がっています。
いろいろ理由があってPCでしかまともに使えないコードになってますので、あくまで参考ということにして標準ライブラリでできることは標準ライブラリでサクっと実装し直したほうが良い気がします。

wiruka

Re: 「敵をメンバに持つ敵」タスクシステムのdeleteタイミング

#4

投稿記事 by wiruka » 9年前

ISLeさんJustyさん返信ありがとうございました、
>>リンク先の記事で言うところのデフラグをすると、Tekiクラスのメンバoptionで保持しているポインタが無効になってしまいます。
あー、よくよく考えれば当然のことですよね。いかんせんCをあまりやらずにC++を勉強し始めた身なので
ここらへん理解が浅かったようです、ご指摘ありがとうございました。

敵を持つ敵を実装したければ同じ処理のまったく別のクラスを作るかいっそ作り直すかのどちらかのようなので
後々のことを考えて作り直すことにします、ありがとうございました。

閉鎖

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