ページ 11

メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 03:18
by 奥兵
現在参考書片手にC++の勉強がてらゲームのソースを書いています。
そのゲームで、キャラクターのクラスを作りコンストラクタが呼ばれた際に画像を読み込むようにしようと思ったのですが
画像を読み込む関数をどう呼んでよいものかわかりません。
参考書片手に考えてみたり、それらしい言葉でググってみたのですが自分では解決できそうにないのでアドバイスお願いします。

コード:

int DemoApp::GameMain(){

	static unit testunit ;//テスト
	 
    static int mode = INITIAL;

	switch(mode){
	case INITIAL://最初に呼ばれます

		 
		testunit.Graphic=DemoApp::LoadGr(L"img/uni001.png");//テスト用仮置き
		//LoadGr
		mode=MAIN;
				 
	break;
	case MAIN://メイン部
		
		wait();
		GetKey(); 
		 
		paint(testunit.Graphic,x,200);//テスト用仮置き
		 			  
	break;

	}
	return 0;
}
unit::unit(){//:Graphic(NULL),HP(0),x(0),y(0),NAME(L""){
	if(unit::Graphic==NULL){
	    //ここでDemoAppのメンバ関数LoadGrにアクセスしたい
	}
}

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 04:10
by トントン
奥兵 さんが書きました:現在参考書片手にC++の勉強がてらゲームのソースを書いています。
そのゲームで、キャラクターのクラスを作りコンストラクタが呼ばれた際に画像を読み込むようにしようと思ったのですが
画像を読み込む関数をどう呼んでよいものかわかりません。
参考書片手に考えてみたり、それらしい言葉でググってみたのですが自分では解決できそうにないのでアドバイスお願いします。

コード:

int DemoApp::GameMain(){

	static unit testunit ;//テスト
	 
    static int mode = INITIAL;

	switch(mode){
	case INITIAL://最初に呼ばれます

		 
		testunit.Graphic=DemoApp::LoadGr(L"img/uni001.png");//テスト用仮置き
		//LoadGr
		mode=MAIN;
				 
	break;
	case MAIN://メイン部
		
		wait();
		GetKey(); 
		 
		paint(testunit.Graphic,x,200);//テスト用仮置き
		 			  
	break;

	}
	return 0;
}
unit::unit(){//:Graphic(NULL),HP(0),x(0),y(0),NAME(L""){
	if(unit::Graphic==NULL){
	    //ここでDemoAppのメンバ関数LoadGrにアクセスしたい
	}
}
LoadGrが意図した通りの挙動をするのであれば
unit::unit内でDemoAppを生成して
呼び出せばよいのではないでしょうか。
ただ、現在の設計で上記の方法を取るのがベターなのかどうかはわかりません。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 04:33
by 奥兵
お返事ありがとうございます。
unitのコンストラクタでDemoAppのインスタンスを制作してアクセスし、
GameMain内11行目のLoadGrのある行をコメントアウトしてみました。

コード:

unit::unit(){//:Graphic(NULL),HP(0),x(0),y(0),NAME(L""){
	if(unit::Graphic==NULL){
	    
		DemoApp hoge;
		Graphic=hoge.LoadGr(L"img/uni001.png");
	}
}
しかしところコンパイルは通るのですがexeが停止してしまいます。
ブレークポイントで読み込み直後のGraphicを調べてみると0x00000000となっており、読み込みに失敗しています。
しかしコメントアウトした行を戻すとexeが正常に動きます。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 10:53
by softya(ソフト屋)
インスタンスの生成タイミングが問題ではないでしょうか?
任意の狙ったタイミングでインスタンスを生成する場合はnewを使ってください。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 14:25
by 奥兵
お返事ありがとうございます。
newでやってみたんですがこういうことであってますか?
画像の読み込みは失敗してしまいました。
LoadGrのソースも見せたほうがいいですかね?

コード:

unit::unit(){//:Graphic(NULL),HP(0),x(0),y(0),NAME(L""){
	if(unit::Graphic==NULL){
	     
		DemoApp *p = new DemoApp ;//
	 	Graphic=p->LoadGr(L"img/uni001.png");
		delete p;
	}
}

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 15:19
by softya(ソフト屋)
インスタンスを何処で管理するか生成と消滅のタイミングは?って事が考えられていないのではないでしょうか。
DemoApp のメンバ関数であるLoadGrはDemoApp のインスタンスを生成するだけで使えるようになるのでしょうか?
他にあるDemoApp のインスタンスと繋がっていると勘違いされている気がします。

DemoApp *pはローカル変数であって他にDemoApp があっても全く関係ないので注意してください。
グローバル変数とローカル変数の関係とこれは同じです。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 15:55
by 奥兵
なるほど、インスタンスを作成しただけでLoadGrが使える筈と勘違いしてました。
確認すると正常に動いていたインスタンスのほうはそれらしいメンバ関数いろいろ呼んでました。
この場合LoadGrを使いたければ動いているほうのインスタンスのポインタを渡せばいいんですかね?

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 16:04
by softya(ソフト屋)
奥兵 さんが書きました:なるほど、インスタンスを作成しただけでLoadGrが使える筈と勘違いしてました。
確認すると正常に動いていたインスタンスのほうはそれらしいメンバ関数いろいろ呼んでました。
この場合LoadGrを使いたければ動いているほうのインスタンスのポインタを渡せばいいんですかね?
unitのインスタンス作成時(コンストラクタが呼ばれるタイミング)で「LoadGrを使いたければ動いているほうのインスタンスのポインタ」が有効でLoadGrが使える状況ならyesです。
そうで無いのならunitのインスタンス生成を遅らせるとかを考えないといけません。
それと、LoadGr()がDemoAppにあるのは何か特別な設計意図があるのでしょうか?オブジェクト指向設計のクラスのメンバとして違和感を感じるのですが。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 16:37
by 奥兵
Direct2Dのサンプルソースを改造しながら書いたためこんな設計になってます。
もともとこのソースが文字と画像を表示するだけのもので、それを改造していったのですが
初期からDemoApp内に画像を読み込む関数や実質のメインループやプロシージャが入っています。
やはりそれらは別のクラスに分けるべきですかね?

コード:

class DemoApp
{
public:

	HRESULT hr;

    DemoApp();
    ~DemoApp();

    HRESULT Initialize();

	int wait();//自分で追加
	int GetKey();//追加
	int GameMain();//追加

    void RunMessageLoop();
	
	ID2D1Bitmap* LoadGr(TCHAR *text);//パッケージ関数

private:
	/**********************************/
	void paint(ID2D1Bitmap *Gr , float x , float y);//パッケージ関数
	void ClearScreen();//パッケージ関数
	/**********************************/
	
    HRESULT CreateDeviceIndependentResources();
    HRESULT CreateDeviceResources();

    HRESULT CreateGridPatternBrush(
        ID2D1RenderTarget *pRenderTarget,
        ID2D1BitmapBrush **ppBitmapBrush
        );

    void DiscardDeviceResources();

    HRESULT OnRender(ID2D1Bitmap *Gr,float x,float y);//もとのコードでは引数無し

    void OnResize(
        UINT width,
        UINT height
        );

    static LRESULT CALLBACK WndProc(
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam
        );

    HRESULT LoadResourceBitmap(
        ID2D1RenderTarget *pRenderTarget,
        IWICImagingFactory *pIWICFactory,
        PCWSTR resourceName,
        PCWSTR resourceType,
        UINT destinationWidth,
        UINT destinationHeight,
        ID2D1Bitmap **ppBitmap
        );

    HRESULT LoadBitmapFromFile(
        ID2D1RenderTarget *pRenderTarget,
        IWICImagingFactory *pIWICFactory,
        PCWSTR uri,
        UINT destinationWidth,
        UINT destinationHeight,
        ID2D1Bitmap **ppBitmap
        );

private:
    HWND m_hwnd;
    ID2D1Factory *m_pD2DFactory;
    IWICImagingFactory *m_pWICFactory;
    IDWriteFactory *m_pDWriteFactory;
    ID2D1HwndRenderTarget *m_pRenderTarget;
    IDWriteTextFormat *m_pTextFormat;
    ID2D1PathGeometry *m_pPathGeometry;
    ID2D1LinearGradientBrush *m_pLinearGradientBrush;
    ID2D1SolidColorBrush *m_pBlackBrush;
    ID2D1BitmapBrush *m_pGridPatternBitmapBrush;
   // ID2D1Bitmap *m_pBitmap;
    ID2D1Bitmap *m_pAnotherBitmap;
};

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 16:54
by softya(ソフト屋)
ちゃんとオブジェクト指向設計しないとクラスの関係が混乱はしてくると思います。
まぁ、C言語のモジュール設計でもそれは同じなんですが変数・インスタンスのスコープと寿命はちゃんと考えないと最終的にグローバルスコープに頼ることになるでしょうから悪い状況になるでしょう。
あちこちで参照するものならシングルトンでグローバルのスコープにしても良いのですが必要最小限のメンバで同属の機能を集めるべきでしょうね。それがオブジェクト化です。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 17:56
by 奥兵
やはりメインループと関数と画像の読み込みや描画が同じクラスなのはまずいですか・・・
この場合はDemoAppクラスをどう書き換えるのが適していますかね?
画像や描画系の処理でまとめてmainだけ別の所に持っていくべきですかね?

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 18:33
by softya(ソフト屋)
奥兵 さんが書きました:やはりメインループと関数と画像の読み込みや描画が同じクラスなのはまずいですか・・・
この場合はDemoAppクラスをどう書き換えるのが適していますかね?
画像や描画系の処理でまとめてmainだけ別の所に持っていくべきですかね?
オブジェクトがどう構成されていれば分かりやすいかは自ずと分かると思います。
DemoAppと言うクラスにグラフィック系の処理が含まれているとは誰も思わないです。なのに含まれているわけですね。
つまり、そういう第三者視点で見た時に名前から連想する直感的な予想から外れていれば良くないプログラムの状態なのです。
この考え方で整理すれば、どうなるべきなのかは分かるのではないでしょうか?
これは変数名、関数名、ファイル名についても言えることです。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 19:13
by 奥兵
なるほど、解釈しやすいことが大事なんですね。
そこをもう少し意識的に書いてみます。とりあえずクラス名はGraphicに直しました。

あと、話が少し戻るのですがunitクラスのコンストラクタからGraphicのLoadGrメンバを呼びたいので
unitのコンストラクタへGraphicクラスのインスタンスのポインタを渡したいのですが、どんな方法がありますか?
ちなみにunitのインスタンスはGraphicのメンバ関数の中で作られてます。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 19:19
by softya(ソフト屋)
奥兵 さんが書きました:なるほど、解釈しやすいことが大事なんですね。
そこをもう少し意識的に書いてみます。とりあえずクラス名はGraphicに直しました。
あと不必要に多機能なクラスもメンテナンス性を下げるので良くないです。
不必要な機能はありませんか? 下記のような。
奥兵 さんが書きました:ちなみにunitのインスタンスはGraphicのメンバ関数の中で作られてます。
それもGraphicクラスという名前から予想できない機能ですね。
つまり良くないです。
奥兵 さんが書きました: あと、話が少し戻るのですがunitクラスのコンストラクタからGraphicのLoadGrメンバを呼びたいので
unitのコンストラクタへGraphicクラスのインスタンスのポインタを渡したいのですが、どんな方法がありますか?
引数付きのコンストラクタを使えば出来ます。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 19:32
by 奥兵
何度も素早いお返事ありがとうございます。
早速やってみますね!

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月23日(日) 23:55
by 奥兵
ソフト屋さんトントンさんアドバイスありがとうございました。
おかげで何とかソースも前に進めそうです。感謝感謝の一言です。
これにて解決とさせて頂きます、本当にありがとうございました。

Re: メンバ関数へのアクセスについての質問

Posted: 2012年9月24日(月) 15:43
by softya(ソフト屋)
奥兵 さんが書きました:ソフト屋さんトントンさんアドバイスありがとうございました。
おかげで何とかソースも前に進めそうです。感謝感謝の一言です。
これにて解決とさせて頂きます、本当にありがとうございました。
これから考えるべき事を補足しておきます。
画像を取得するためのクラスではなく、インスタンスとして画像を保持・描画するクラスなどにした方がより抽象化できます。
これはデータ構造の隠蔽、ロジックの隠蔽の隠蔽をより進めるために必要なことです。

例えばstd::stringなどは文字列がcharポインタである事を隠蔽しようとしています。それと同じように画像の物理属性も隠蔽できるはずです。
そうすればDirectXのバージョンや使用ライブラリが変わっても一部のクラスを変更するだけでプログラム全体に影響が出ない形にすることができます。