C++ 連結リストと派生クラスについて

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

C++ 連結リストと派生クラスについて

#1

投稿記事 by ペキロゲン » 14年前

現在C++にてシューティングゲームを作っているのですが、良く分からないことがあり質問しました。

現状を説明しますと
敵単体の基本クラスであるEnemyDataクラスがあり、そこにはmove()関数いうその敵の行動を制御する関数があり、これは
virtual void move(){}と基本クラスでは何もしません。
そして、その派生クラスEnemy1にてこの関数をオーバーライドして、そこでmove()関数の中身を書き実際に行動させるとします。

ここで敵全体を制御するEnemyControlクラスがあり、そこにはEnemyDataクラスのリストであるlist<EnemyData> enemy_listがあります。
ここで基本クラスのリストには派生クラスも要素として入れることができるので、
Enemy1 new_enemy;
enemy_list.push_back(new_enemy);
とすれば、enemy_listにてEnemy1も扱うことが出来ると思いました。

しかし、実際にイテレーターをlist<EnemyData>::iterator enemy;と宣言し
enemy=enemy_list.begin();
enemy->move();
とした場合、要素として入れたはずのEnemy1のmove()ではなく、基本クラスのEnemyDataのmove();関数が実行されてしまいました。

これは一体何がいけないのでしょうか?
Enemy1クラスのmove();を実行するにはどのようにすればいいのでしょうか?
回答お願いします。

ペキロゲン

Re: C++ 連結リストと派生クラスについて

#2

投稿記事 by ペキロゲン » 14年前

ちなみにこれがリストではなく配列の場合

EnemyData* array[100];
Enemy1 enemy1;

array[0]=&enemy1;
array[0]->move();

みたいにすれば、Enemy1クラスのmove()が実行されると思います。
つまりこれと同じことをリストでも行いたいのですが、それをどのようにすればいいのか分からないといった感じです。

アバター
a5ua
記事: 199
登録日時: 14年前

Re: C++ 連結リストと派生クラスについて

#3

投稿記事 by a5ua » 14年前

コード:

EnemyData* array[100];
これは、「EnemyData*」の配列ですよね。
同じように、「EnemyData*」のリストとしなければいけません。

したがって、

コード:

list<EnemyData*> enemy_list;
とし、

コード:

Enemy1 new_enemy;
enemy_list.push_back(&new_enemy);
(*enemy_list.begin())->move();
もしくは、

コード:

enemy_list.push_back(new Enemy1());
(*enemy_list.begin())->move();
// new したものは delete
delete *enemy_list.begin();
のようになります。

ポインタによるポリモーフィズムを使うときの注意点としては、
派生クラスへのポインタを基底クラスへのポインタを通してdeleteする場合は、
基底クラスのデストラクタはvirtualである必要があります。

ペキロゲン

Re: C++ 連結リストと派生クラスについて

#4

投稿記事 by ペキロゲン » 14年前

>>a5uaさん
回答ありがとうございます!
後者のnewを使ったやり方で記述することで、おかげで無事実行することができました!
助かりました。本当にありがとうございます;;



ちなみに、もしよろしければさらに質問なのですが、
あるリストの要素の削除をイテレーターitを使ってする場合、~~list.erase(it)のようにすることで削除出来ますが、
お教えいただいたnewを使ってリストに要素をpush_back()をした場合、削除するときはやはり
erase()を使わずに、deleteで消さなければならないのでしょうか?

それと前者のやり方の場合、ビルドの際エラーは起きませんでしたが、プログラム実行後、不正アクセス等で中断されたりました。
これはプログラムを見てない以上お答えしかねるとは思いますが、何かしら原因分かりますでしょうか?

アバター
a5ua
記事: 199
登録日時: 14年前

Re: C++ 連結リストと派生クラスについて

#5

投稿記事 by a5ua » 14年前

ペキロゲン さんが書きました: あるリストの要素の削除をイテレーターitを使ってする場合、~~list.erase(it)のようにすることで削除出来ますが、
お教えいただいたnewを使ってリストに要素をpush_back()をした場合、削除するときはやはり
erase()を使わずに、deleteで消さなければならないのでしょうか?
eraseすると、そのイテレータが指すポインタにはアクセスできなくなるわけですから、
ポインタをdeleteをした後に、その要素をeraseする必要があります。

コード:

list<EnemyData *> enemy_list;

void MoveEnemies()
{
	list<EnemyData *>::iterator it = enemy_list.begin();
	while (it != enemy_list.end()) {
		(*it)->move();
		if ((*it)->is_dead()) {
			delete *it;
			it = enemy_list.erase(it);
		} else {
			++it;
		}
	}
}
ペキロゲン さんが書きました: それと前者のやり方の場合、ビルドの際エラーは起きませんでしたが、プログラム実行後、不正アクセス等で中断されたりました。
これはプログラムを見てない以上お答えしかねるとは思いますが、何かしら原因分かりますでしょうか?
もし、以下のコードにおけるAddNewEnemy1のような使い方をしている場合、
ローカル変数の寿命により、アクセスできなくなることは考えられます。

コード:

list<EnemyData *> enemy_list;

void AddNewEnemy1()
{
	Enemy1 new_enemy;
	enemy_list.push_back(&new_enemy);
	//new_enemyが破棄されるため、リスト内のEnemyDataにアクセスできる保証は全くなくなる
}

void AddNewEnemy2()
{
	enemy_list.push_back(new Enemy1());
	// newしたオブジェクトは明示的にdeleteするまで存在する
}

void MoveEnemies()
{
	if (some_condition) {
		AddNewEnemy1();
		AddNewEnemy2();
	}
	for (list<EnemyData *>::iterator it = enemy_list.begin(); it != enemy_list.end(); ++it) {
		(*it)->move();
	}
}
まあ、newによる生成を使うのが普通だと思います。

スマートポインタをリストの要素にすると、明示的なdeleteは不要になりますが、
詳細はここでは記しませんので、興味があったら調べてみてください。

ペキロゲン

Re: C++ 連結リストと派生クラスについて

#6

投稿記事 by ペキロゲン » 14年前

なるほど。非常に分かりやすい回答ありがとうございます!
二つとも調度私がやりたかったこと・やっていたことで、
おかげ様で良く分からなかった二つの疑問点も一気にスッキリしました。
何度もお答えいただき本当に助かりました。ありがとうございました!


最後のスマートポインタに関しては自分で調べてみますね^^;

閉鎖

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