継承元のクラスのインスタンス化について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
EKISUKE
記事: 108
登録日時: 13年前

継承元のクラスのインスタンス化について

#1

投稿記事 by EKISUKE » 12年前

継承元(親)のクラスでインスタンス化した物を子クラス同士でも同じものを共有できませんか?

StageBase.h (ステージ基底)

コード:

class StageBase
{
public:
    Map		    map;			//!< マップインスタンス化
	Bouei		bouei;			//!< 防衛インスタンス化
	Back		back;			//!< 背景インスタンス化
	Player		player;			//!< プレイヤーインスタンス化
	Special		special;		//!< 必殺技インスタンス化
	HitManager	hit;			//!< ヒット管理インスタンス化
	Recode		recode;			//!< タイム、スコアインスタンス化
	EnemyBase*	gpEnemy[ENEMY_COUNT_MAX];		//	敵配列
public:
	ofTrueTypeFont myFont_64;		//!< フォントクラスインスタンス化

	StageBase(void);
	virtual ~StageBase(void);
	virtual void init();				//!< 初期化
	virtual void update();				//!< 更新
	virtual void draw();				//!< 描画
};
今作っているゲームで、ゲームのメインループ(Stageというクラス)を
StageTitle、StageGame、StageClear、StageGameOverという4つのクラスにわけ、メインループではswitch文でゲームの流れを見やすくしようとしています。
Stage、StageTitle、StageGame、StageClear、StageGameOverはそれぞれStageBaseを継承していて、
Stageのなかでは下のようなコードにしたいのです。

Stage.cpp

コード:

void Stage::init()
{
   StageBaseでインスタンス化したクラスのinit
}

void Stage::update()
{
   switch(ステージの状態){
       case Title:                        //   タイトル画面
                StageTitleのupdate();
                break;
       case Game:                      //   ゲーム状態
                StageGameのupdate();
                break;
       case Clear:                       //   ゲームクリア状態
                StageClearのupdate();
                break;
       case GameOver:               //   ゲームオーバー状態
                StageGameOverのupdate();
                break;
       }
}
void Stage::draw()
{
      switch(ステージの状態){
       case Title:                        //   タイトル画面
                StageTitleのdraw();
                break;
       case Game:                      //   ゲーム状態
                StageGameのdraw();
                break;
       case Clear:                       //   ゲームクリア状態
                StageClearのdraw();
                break;
       case GameOver:               //   ゲームオーバー状態
                StageGameOverのdraw();
                break;
       }
}
インスタンス化したクラス(これを共有したい)

コード:

    Map		    map;			//!< マップインスタンス化
	Bouei		bouei;			//!< 防衛インスタンス化
	Back		back;			//!< 背景インスタンス化
	Player		player;			//!< プレイヤーインスタンス化
	Special		special;		//!< 必殺技インスタンス化
	HitManager	hit;			//!< ヒット管理インスタンス化
	Recode		recode;			//!< タイム、スコアインスタンス化
	EnemyBase*	gpEnemy[ENEMY_COUNT_MAX];		//	敵配列
どのクラスで共有したいのかといいますと、Stage、StageTitle、StageGame、StageClear、StageGameOverの5つのクラスで、
なにを共有したいかといいますと、StageBaseでインスタンス化しているクラスを共有したいのです。
ですが、どのようにすればこの5つのクラスで同じ物を共有できるのかわかりません。
プログラムは組んでみたのですが、継承したクラスではそれぞれがコピーをとって違うものを持っているようで、うまく共有ができません。

組んでみたプログラム
Stage.h

コード:

class Stage : public StageBase, public ofBaseApp
{
public:
	//	インスタンス化一覧
	StageTitle		title;
	StageGame		game;
	StageClear		clear;
	StageOver		over;

	//	ステージ切り替え用列挙体
	enum STAGE_MODE{
		STAGE_TITLE,
		STAGE_GAME,
		STAGE_GAMEOVER,
		STAGE_CLEAR,
	};

public:
	Stage(void);				//!< コンストラクタ
	virtual~Stage(void);		//!< デストラクタ
	bool	addEnemy(EnemyBase* p);
	virtual void init();		//!< 初期化
	virtual void update();		//!< 更新
	virtual void draw();		//!< 描画
	void start_draw();	//!< タイトル画面描画
	bool clear_flag;


	void mouseMoved(int x, int y );
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);


private:
	STAGE_MODE stage_mode;
};
Stage.cpp

コード:

//==============================================================
//!	@file	Stage.cpp
//!	@breif	ステージの流れ
//!	@author	YukiIshigaki
//==============================================================
#pragma once
#include"../../HeaderMain.h"

//EnemyBase*	gpEnemy[ENEMY_COUNT_MAX];		//	敵配列

//--------------------------------------------------------------
//! 敵配列に追加する
//!@param	[in]	p	追加する敵クラス
//!@param	true	成功
//!@param	false	失敗(満杯)
//--------------------------------------------------------------
bool	Stage::addEnemy(EnemyBase* p)
{
	for( int i = 0; i < ENEMY_COUNT_MAX; i++){
		if( gpEnemy[i] != NULL ) continue;

		//---- find
		gpEnemy[i] = p;
		return true;
	}
	return	false;
}


//	コンストラクタ
Stage::Stage(void)
{
	

		ofSetFrameRate(60);	//	フレーム数を固定
	//==========================================================
	//	スタート、ゲームオーバー等の初期化
	//==========================================================
		//myFont_64.loadFont("font_data/cooperBlack.ttf",64);		//	フォントデータ読み込み

	//==========================================================
	//	マップ初期化
	//==========================================================
		map.init();

	//==========================================================
	//	背景初期化
	//==========================================================
		back.setup();

	//==========================================================
	//	プレイヤー初期化
	//==========================================================
		
	//==========================================================
	//	防衛初期化
	//==========================================================
		bouei.setup();

	//==========================================================
	//	敵初期化
	//==========================================================
		
		for( int i=0; i<10; i++){

			EnemyBase*	p = new Crow;
			if( addEnemy(p) == false){
				delete p;
			}
		}
		for( int i=0; i<10; i++){

			EnemyBase*	p = new Ghost_Big;
			if( addEnemy(p) == false){
				delete p;
			}
		}
		for( int i=0; i<10; i++){

			EnemyBase*	p = new Ghost_Mini;
			if( addEnemy(p) == false){
				delete p;
			}
		}
	//==========================================================
	//	タイム、スコア初期化
	//==========================================================
		recode.init();

	clear_flag = false;
	clear.init();
	stage_mode = STAGE_TITLE;		//	ステージモード初期化
}

//	デストラクタ
Stage::~Stage(void)
{
	delete [] * gpEnemy;
}

//	初期化
void Stage::init()
{
	// コンストラクタと同じ
}



//	更新処理
void Stage::update()
{
	switch( stage_mode )
	{
	case STAGE_TITLE:
		{
			title.update();

			if(GetKeyState(VK_RETURN)& 0x8000){	//	エンターキーを押したら
			stage_mode = STAGE_GAME;		//	ゲーム状態に移行
			}
		}
		break;
	case STAGE_GAME:
		{

			game.update(gpEnemy);
		}
		break;

	case STAGE_CLEAR:
		{
			//clear.update();


			SCROLL_SPEED = 0;			//	スクロールをしないように
			bouei.bouei_clear();		//!< 防衛が走り去る処理
			bouei.animation(0,3);			//	アニメーション
			//==========================================================
			// 敵更新処理
			//==========================================================
			for( int i=0; i<ENEMY_COUNT_MAX; i++ ){
				if( gpEnemy[i] == NULL ) continue;
				gpEnemy[i]->update();
				gpEnemy[i]->enemysetMove();
			}

			player.update();	//	プレイヤー更新

			if(bouei._pos._x > 1024.0f + bouei._radius){
				//	ゲームクリア処理(ゲームクリアの文字の移動やリザルト)
				clear.update();
			}

			if(GetKeyState(VK_RETURN)& 0x8000){	//	エンターキーを押したら
				for(int i=0; i<ENEMY_COUNT_MAX; i++){
					gpEnemy[i] = NULL;	//	敵のデリート
				}
				init();	//	全初期化
				stage_mode = STAGE_TITLE;		//	スタート画面に戻る
			}
		}
		break;
	case STAGE_GAMEOVER:
		{
			over.update();
			player.update();	//	プレイヤー更新
			
			if(GetKeyState(VK_RETURN)& 0x8000){	//	エンターキーを押したら
				for(int i=0; i<ENEMY_COUNT_MAX; i++){
					gpEnemy[i] = NULL;	//	敵のデリート
				}
				init();	//	全初期化
				stage_mode = STAGE_TITLE;		//	スタート画面に戻る
			}
		}
		break;
	}
}

//	描画処理
void Stage::draw()
{

	switch( stage_mode )
	{
	case STAGE_TITLE:
		{
			title.draw();
			player.draw();
		}
		break;
	case STAGE_GAME:
	case STAGE_CLEAR:
		{
			ofSetColor(255,255,255);
			back.draw();	//!< 背景描画
			game.draw(gpEnemy);
			clear.draw();
			player.draw();		//!< 操作キャラ(Player)の描画
		}
		break;

	case STAGE_GAMEOVER:
		{
			over.draw();
			player.draw();
		}
		break;
	default:
		cout<<"ステージ状態がエラーを起こしています"<<endl;
		break;
	}


}

void Stage::mouseMoved(int x, int y ){
	player.setPos(x,y);
}

void Stage::mousePressed(int x, int y, int button){

	player.setPos(x,y,button);
	Vector2 pos;
	pos._x = x;
	pos._y = y;
	player.active = true;
}

void Stage::mouseReleased(int x, int y, int button){
	player.active = false;
}
すごく醜いコードですみません。
上に書いた共有したい部分をうまく共有できていないため、Stageで値が変わった変数が、
StageGameの方では変わってなかったので、共有して同時に値が変わるようにしたいです。

出来れば関数(game.updateのように)の引数に渡したりせずに、継承したクラスでは共有したい部分を共通で使えるようにはできませんか?

うまく説明できていなくてすみません。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#2

投稿記事 by softya(ソフト屋) » 12年前

コードを見てませんが、「継承元(親)のクラスでインスタンス化した物を子クラス同士でも同じものを共有」はグローバル変数と変わらないと思います。
グローバル変数にしてはダメなのですか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#3

投稿記事 by EKISUKE » 12年前

softya(ソフト屋) さんが書きました: コードを見てませんが、「継承元(親)のクラスでインスタンス化した物を子クラス同士でも同じものを共有」はグローバル変数と変わらないと思います。
グローバル変数にしてはダメなのですか?
回答有り難うございます。
その方法は考えていませんでした。

グローバルだと、他のところで変にいじってしまうと怖いので、
継承したクラスのみ共通で使えるというふうにしたいのですが、可能でしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#4

投稿記事 by softya(ソフト屋) » 12年前

C++であればソースファイル(cpp)内にstaticな変数を用意すればファイルスコープでしか見えません。
つまり、そのcpp内のメンバ関数からしか見えない共通の変数が実現できます。

あるいは、クラスにprivateでstaticな変数を用意しても同じ様に共通化出来ます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#5

投稿記事 by EKISUKE » 12年前

softya(ソフト屋) さんが書きました:C++であればソースファイル(cpp)内にstaticな変数を用意すればファイルスコープでしか見えません。
つまり、そのcpp内のメンバ関数からしか見えない共通の変数が実現できます。

あるいは、クラスにprivateでstaticな変数を用意しても同じ様に共通化出来ます。
回答ありがとうございます。

なるほど、staticがありましたね。

もしかして、staticは共有したい変数全てに用意するということですか?
インスタンス化したクラスごと共有という形はできないですか?(externみたいに)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#6

投稿記事 by softya(ソフト屋) » 12年前

EKISUKE さんが書きました:インスタンス化したクラスごと共有という形はできないですか?(externみたいに)
単位がよくわかないのですが、class Stageのインスタンス化したもの全てで共有なんですよね?
なのでprivateでstaticはどうでしょう?って事なのですが。
「C++編(言語解説) 第19章 静的メンバ」
http://www.geocities.jp/ky_webid/cpp/language/019.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#7

投稿記事 by EKISUKE » 12年前

softya(ソフト屋) さんが書きました:単位がよくわかないのですが、class Stageのインスタンス化したもの全てで共有なんですよね?
なのでprivateでstaticはどうでしょう?って事なのですが。
共有したいのはclass StageBaseの

コード:

class StageBase
{
public:
    Map         map;            //!< マップインスタンス化
    Bouei       bouei;          //!< 防衛インスタンス化
    Back        back;           //!< 背景インスタンス化
    Player      player;         //!< プレイヤーインスタンス化
    Special     special;        //!< 必殺技インスタンス化
    HitManager  hit;            //!< ヒット管理インスタンス化
    Recode      recode;         //!< タイム、スコアインスタンス化
    EnemyBase*  gpEnemy[ENEMY_COUNT_MAX];       //  敵配列
public:
    ofTrueTypeFont myFont_64;       //!< フォントクラスインスタンス化
 
    StageBase(void);
    virtual ~StageBase(void);
    virtual void init();                //!< 初期化
    virtual void update();              //!< 更新
    virtual void draw();                //!< 描画
};
インスタン化している部分

コード:

    Map         map;            //!< マップインスタンス化
    Bouei       bouei;          //!< 防衛インスタンス化
    Back        back;           //!< 背景インスタンス化
    Player      player;         //!< プレイヤーインスタンス化
    Special     special;        //!< 必殺技インスタンス化
    HitManager  hit;            //!< ヒット管理インスタンス化
    Recode      recode;         //!< タイム、スコアインスタンス化
    EnemyBase*  gpEnemy[ENEMY_COUNT_MAX];       //  敵配列
すべてです。
StageBaseを継承しているクラスはStage、StageTitle、StageGame、StageClear、StageGameOverで、
それぞれのソースファイルで上のインスタンス化したクラスのメンバを使っています。
なので、StageBaseのインスタンス化したクラスを継承している5つのクラスで同じ物を使いたいのです。
それが、privateのstaticに当たるのでしょうか・・・?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#8

投稿記事 by softya(ソフト屋) » 12年前

うーん。オブジェクト指向的に設計がマズイ気がだいぶする話ですね。
原点に帰りますが、Stage、StageTitle、StageGame、StageClear、StageGameOverが全部のインスタンスにアクセスできる必要性は皆無だと思います。
独立したクラスにしてはダメなんでしょうか?

こういうデザイン・パターンを使うとか。
「デザインパターン編 第20章 Mediatorパターン」
http://www.geocities.jp/ky_webid/design ... n/020.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#9

投稿記事 by EKISUKE » 12年前

コード:

void Stage::init()
{
   StageBaseでインスタンス化したクラスのinit
}
 
void Stage::update()
{
   switch(ステージの状態){
       case Title:                        //   タイトル画面
                StageTitleのupdate();
                break;
       case Game:                      //   ゲーム状態
                StageGameのupdate();
                break;
       case Clear:                       //   ゲームクリア状態
                StageClearのupdate();
                break;
       case GameOver:               //   ゲームオーバー状態
                StageGameOverのupdate();
                break;
       }
}
void Stage::draw()
{
      switch(ステージの状態){
       case Title:                        //   タイトル画面
                StageTitleのdraw();
                break;
       case Game:                      //   ゲーム状態
                StageGameのdraw();
                break;
       case Clear:                       //   ゲームクリア状態
                StageClearのdraw();
                break;
       case GameOver:               //   ゲームオーバー状態
                StageGameOverのdraw();
                break;
       }
}
こういうふうに最終的には設計したいのですが、これではまずいということですか?
softya(ソフト屋) さんが書きました:独立したクラスにしてはダメなんでしょうか?
独立ということは Mediatorに使いたい部分をインスタンス化してそこを介してStage、StageTitle、StageGame、StageClear、StageGameOverが情報を交換?するということですか?
イマイチイメージができないです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#10

投稿記事 by softya(ソフト屋) » 12年前

オブジェクト指向的には動作に関係ないインスタンスを内包しているのがBADです。それこそ、この構成ならStageクラスが各インスタンスを保持している理由がありません。
とりあえず入れておけ的な設計をせず、ちゃんとインターフェイスでの情報の流れを設計をまずしてみてください。

Mediatorが良く分からなければ、龍神録C++の様にしても良いでしょう。
「龍神録の次回作 [龍神No.1] • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=53&b=2391
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#11

投稿記事 by EKISUKE » 12年前

URLを見たのですが、まだイメージがわきません。

Stageでやりたいのはゲームの流れをそれぞれのクラスで持って、それを呼び出してゲームの流れを見やすくしようとしているのですが、
どのゲーム状態にも必要なマウス座標の取得などはStage内でやっています。
そのマウスのクラスの中の情報はStageTitle,StageGame,StageClear,StageGameOverのそれぞれのクラスで変更したりします。
なので、StageBaseでインスタンス化をすべて行い継承していればインスタンス化しているクラスの情報を変更できるようにしたいと思ってます。
でも、それぞれのクラスでコピーが取られるようなので、共有する方法を知りたかったのですが、それではオブジェクト指向的にまずいということですよね?

StageBaseとStage,StageTitle,StageGame,StageClear,StageGameOverの間にMediatorを作り、そこで情報を変更したりするということですか?
この場合すべてのクラスのインスタンス化はMediatorで行うのでしょうか? それと、どの状況でも中の情報が必要なクラスはどこでインスタンス化すべきですか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#12

投稿記事 by softya(ソフト屋) » 12年前

親クラスでインスタンスを生成して子クラスで共用するのはどうやっても分かりづらいインターフェイスだと思うんですよね。具体例が挙げれませんが、すごく危険な香りもします。
どちらにしてもグローバル変数の一種ですので、最大の問題点である誰が変更したかプログラムの流れとタイミング分かりづらいと言う問題の方が便利より大きいと思います。
クラスが持つ他のクラスのインスタンスって必要最低限にすべきだと思いますし無いのが一番です。

こうすれば正解というのは私の中にも無いのですが、共用出来るインスタンスであって1つしか存在しないならシングルトンの適用も考慮します。ただ、シングルトンはグローバル変数ですのであちこちから参照は良いとして書き換える方法だけは厳密に制限する必要があります。
「シングルトンパターンの誘惑に負けない - Strategic Choice」
http://d.hatena.ne.jp/asakichy/20110803/1312323474

>StageBaseとStage,StageTitle,StageGame,StageClear,StageGameOverの間にMediatorを作り、そこで情報を変更したりするということですか?

共通化出来る単位でMediatorを最小限用意しますが、プログラムの構造から変えないと出来なさそうですね。

>この場合すべてのクラスのインスタンス化はMediatorで行うのでしょうか? それと、どの状況でも中の情報が必要なクラスはどこでインスタンス化すべきですか?

だれがインスタンスを生成しても良いですがインスタンスの保持はMediatorです。
全体的に使うなら上に書いたシングルトンも考慮しましょう。

私もオブジェクト指向は試行錯誤の連続ですが、図を書いてみるとすっきりしたりします。

一応元の希望の事を実現できる方法も書いておきますが避けたほうが良いと思います。

コード:

#include <iostream>
#include <string>
using namespace std;

class A {
protected:
	static string m_str;
public:
	A() { m_str = "a"; };
	void set(string str) {
		A::m_str = str;
	};
};
string A::m_str;

class B : public A {
public:
	void draw() {
		cout << A::m_str << endl;
	};
};

class C : public A {
public:
	void draw() {
		cout << A::m_str << endl;
	};
};

void main()
{
	B inst1;
	C inst2;
	
	inst1.draw();
	inst2.draw();
	inst1.set("bb");
	inst1.draw();
	inst2.draw();
	
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 継承元のクラスのインスタンス化について

#13

投稿記事 by ISLe » 12年前

そもそもStageTitle、StageGame、StageClear、StageGameOverがswitchブロックに列挙されているのでは独立性が確保されておらず、インターフェースも機能していません。
オブジェクト指向としてまずいというよりオブジェクト指向ではないと思います。

現状はメンバをクラス名でグループ化しているだけの状況だと言えます。
むしろメンバをバラしてどのようにグループ化すべきか再検討することをお勧めします。

EKISUKE
記事: 108
登録日時: 13年前

Re: 継承元のクラスのインスタンス化について

#14

投稿記事 by EKISUKE » 12年前

色々プログラムを読んだり、いろんな人に質問したりして、結局タイトルと、ゲームは分けるけれども、
ゲームクリア、ゲームオーバーの処理は関数化して、ゲームから呼び出すという結論に至りました。
色々と答えて頂いたのに、すみません。

色々学校の先生にもこのことについて質問していると、
ゲームクリア画面ではまだ後ろでゲーム画面が動いているので、ゲームと独立させるのはまずいんじゃないかという答えがあり、
違う方法(ポインタを使ってゲーム遷移など)を試してみたりしたのですが、あまり、どれも整理されていないような気がするので、
タイトルと、ゲームは分けて、ゲームクリアと、ゲームオーバーは関数化して呼び出すということにしました。

答えていただき本当に有難うございます。

閉鎖

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