クラスの関数内の変数について

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

クラスの関数内の変数について

#1

投稿記事 by とっち » 11年前

あるクラス内の関数でクラスのインスタンスが破棄されるまで
変数の値を保持したいです。
通常は

コード:

class CTest{
	int hensuu;
public:
	CTest();
	~CTest();
	void foo();
};

void CTest::foo(){
	// hensuuを使って処理
}
というようにクラス変数を使えばいいのですが、
この変数hensuuが関数foo()内でしか使われないとき
クラス変数にするのはなんだかなぁと思いました。

クラス変数にするとfoo()以外のクラス関数からも
hensuuの値を書きかえられ好ましくありません。

かといっって

コード:

void CTest::foo(){
	static int hensuu;
	// hensuuを使って処理
}
のようにstatic宣言するのはよろしくないですよね

なにかスマートな書き方はないでしょうか?

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: クラスの関数内の変数について

#2

投稿記事 by h2so5 » 11年前

本当にそんなに細かいアクセスコントロールが必要なのかは疑問ですが。

hensuuに対して限定的なアクセスを提供するという意味では、
hensuuをクラスでラップして、そのクラス変数にfoo()と同等の機能を実装するというのはどうでしょう。
CTestのメンバの情報が必要な場合は引数で渡せばよいです。

コード:

class CTest{
    class Holder {
        private:
            int hensuu;
        public:
            void foo() {
                // hensuuを使って処理
            }
    } holder;
public:
    CTest();
    ~CTest();
    void foo();
};
 
void CTest::foo(){
    holder.foo();
}
とっち さんが書きました: かといっって

コード:

void CTest::foo(){
	static int hensuu;
	// hensuuを使って処理
}
のようにstatic宣言するのはよろしくないですよね
よろしくない、というかstaticにしたら値が全インスタンスで共通になってしまうので実現不可能ですよね。

たかぎ
記事: 328
登録日時: 13年前
住所: 大阪
連絡を取る:

Re: クラスの関数内の変数について

#3

投稿記事 by たかぎ » 11年前

こんなのでどうでしょう?

コード:

struct fooImpl{
    int hensuu;
    void foo();
};

class CTest : fooImpl{
public:
    CTest();
    ~CTest();
    using fooImpl::foo;
};
必要なだけ、いくつでも多重継承できますので、希望通りのことが実現できると思います。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: クラスの関数内の変数について

#4

投稿記事 by h2so5 » 11年前

>たかぎさん

その方法ですとCTestのメソッド内からhensuuに自由にアクセスできるように見えますが...?

たかぎ
記事: 328
登録日時: 13年前
住所: 大阪
連絡を取る:

Re: クラスの関数内の変数について

#5

投稿記事 by たかぎ » 11年前

h2so5 さんが書きました:>たかぎさん

その方法ですとCTestのメソッド内からhensuuに自由にアクセスできるように見えますが...?
失礼、その通りです。
privateを付けておくのを忘れていました。

とっち
記事: 56
登録日時: 12年前
住所: 岡山

Re: クラスの関数内の変数について

#6

投稿記事 by とっち » 11年前

返信ありがとうございます。

>h2so5さん
なるほど、内部クラスで管理すればよかったのですね!

>たかぎさん
これはよさそうですね
ただclass CTestをヘッダファイルに書くような場合、
struct fooImplも同じヘッダ内に書かないといけないのがちょっと嫌ですね(^^;)

私としてはこのようなことがしたかったのです
例えばRPGゲームだとして

コード:

// main.cpp
#include "DxLib.h"

int WINAPI WinMain(略){
	// いろいろ省略
	int state;
	CMap mapMove;// マップに関するクラス
	CMenu menu;// メニューに関するクラス
	while(ProcessMessage()==0){
		// メインループ
		switch(state){
		case 0:
			// マップに関するクラスで処理
			mapMove.Process();
			// 戻り値によってstateを変更
			break;
		case 1:
			// メニューに関するクラスで処理
			menu.Process();
			// 戻り値によってstateを変更
			break;
		case 2:
			break;
		}
	}
}

// menu.h
class CMenu{
	int state;
	void foo1();
	void foo2();
	void foo3();
public:
	int Process();
};

// menu.cpp

int CMenu::Process(){
	switch(state){
	case 0:
		foo1();
		break;
	case 1:
		foo2();
		break;
	case 2:
		foo3();
		break;
	}
	return 0;// 戻り値によってメインループのstateを変更(今は適当です)
}

void CMenu::foo1(){
	// ここで状態変数によって状態を変えたい
	switch(state_2){
	case文:
	}
}

コード適当で申し訳ないです。
menu.cppのCMenuのfoo1関数で状態によって場合分けしたいのです。
foo1関数内の状態で行う処理が大きいのならばfoo4というクラス関数を作成してもいいのですが、
ちょこっとだけの処理の場合めんどくさいなぁと思って質問しました。

このような場合、h2so5さんやたかぎさんが提示してくださったような方法が良いのでしょうか?
それともfoo4という関数を作ったほうがいいのでしょうか?

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: クラスの関数内の変数について

#7

投稿記事 by h2so5 » 11年前

正直何がやりたいのか分からないです。state_2とかどこから出てきたのかわからないですし。
メインループのswitchは冗長なので、Stateパターンを使ったほうがすっきりすると思います。

とっち
記事: 56
登録日時: 12年前
住所: 岡山

Re: クラスの関数内の変数について

#8

投稿記事 by とっち » 11年前

>h2so5さん
失礼しました。
言葉ではどうもうまく伝えられませんでした。

面倒なのでプロジェクトごとあげてみます
いろいろファイルありますが結構何も書いてないファイルが多いです。

ここで質問したいのはmenu.cpp,menu.hについてです。
CMenuのItemSelect()関数が問題のところです。
CWinMgr型のwinという変数についてですが、ウィンドウに関する処理を行っています。
基本的にソフト屋さんのRPG講座を参考に作っているのですが、
Draw()で今あるウィンドウをすべて描画、New()で新たにウィンドウを登録、
Delete()で削除、Set()でstd::string型の文字列か、std::list<std::string>型のリストを代入します。
また、New()は新しく作ったウィンドウのウィンドウ番号を返し、その番号をDelete()
に渡してあげることで削除できます。
GetCursor()は決定キーが押されたら今のカーソルの位置が返りそれ以外は-1が返ります
MoveCursor()は今のカーソルの位置が返ります

コード:

void CMenu::ItemSelect(){
	// timeはCSceneBaseで定義されており、SetState()関数が呼ばれるたびに
	// つまり関数が初めに呼ばれたタイミングで0になります。
	if(time==0){
		Assert(useNo!=-1,"CMenu: アイテム番号が異常です");
		generalState=0;// これが問題のstate変数です。(今はクラス変数としています)
		unsigned num=party->GetPartyNum();
		paramWinNo=win.New(400,30*num+45,170,90);
		targetWinNo=win.New(400,40,170,30*num);
		std::list<std::string> lstr;
		for(unsigned i=0;i<party->GetPartyNum();i++)
			lstr.push_back(party->GetParam(i).name);
		win.Set(lstr);
	}

	int no=win.GetCursor();
	switch(generalState){
		case 0:{// 初期状態(文字列を代入しています)
			win.Set(paramWinNo);
			std::list<std::string> lstr;
			lstr.push_back(ValueOfString("HP %3d/%3d",party->GetParam(no).hp,party->GetParam(no).hpMax));
			lstr.push_back(ValueOfString("MP %3d/%3d",party->GetParam(no).mp,party->GetParam(no).mpMax));
			win.Set(lstr,16,20,false);
			win.Set(targetWinNo);
			generalState++;
			break;
		}
		case 1:
			targetNo=win.MoveCursor();
			if(targetNo!=-1)// 決定キーが押されたら
				generalState++;
			else if(win.GetCursor()!=no)// もしカーソルが上下に移動したら
				generalState=0;

			if(CKey::CheckKey(KEY_CANCEL)==1){
				win.Delete(targetWinNo);
				win.Delete(paramWinNo);
				SetState(&CMenu::Item);
			}

			break;
		case 2:// ItemUse処理
			win.Delete(targetWinNo);
			win.Delete(paramWinNo);
			targetWinNo=win.New(360,90,150,60);
			if(party->UseItem(useNo,targetNo))
				win.Set(ValueOfString("%sを使用した",party->GetItemName(useNo).c_str()));
			else
				win.Set("使えない!");
			generalState++;
			break;
		case 3:
			if(win.UpdateMsg(MESSAGE_SPEED)){// 文字列を表示させていく
				win.Delete(targetWinNo);
				SetState(&CMenu::Item);
			}
			break;
	}
}
generalStateというのを何とかしたいのです。
ひどく汚くて読みにくいとは思いますがお願いします。
添付ファイル
RPG.zip
(303.34 KiB) ダウンロード数: 102 回

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: クラスの関数内の変数について

#9

投稿記事 by h2so5 » 11年前

処理の流れがあまり良く把握できていないのですが、コードを見る限りそんなにカプセル化されているようにも見えないので、generalStateを特に気にする理由がよく分からないですね。
CMenuはクラスの設計としては洗練されてないかもしれませんが、カプセル化を重視しないという方針であればgeneralStateを特別扱いする理由が薄いと思います。

少し話がそれますが、ステートの移行にはenumを使用して代入する方がコードとして明確だと思います。
generalState++;では次のステートに移行しているという意味は分かりますが、移行先のステートが分からないので。

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: クラスの関数内の変数について

#10

投稿記事 by GRAM » 11年前

そもそも論なんですけど、メンバ変数が特定の関数にのみ必要で、しかもその変数をそのクラスが保持することに違和感を覚えるのだとしたら
それはより細かくクラス分けをするべき時なんじゃないですかね?
もちろん規模によるでしょうが。
その意味ではたかぎさんのコードが近いですけれど、
自分的には普通にコンポジットパターンでいいんじゃないかと思ってしまいます。

とっち
記事: 56
登録日時: 12年前
住所: 岡山

Re: クラスの関数内の変数について

#11

投稿記事 by とっち » 11年前

>h2so5さん
そうですかね。
たしかに私が神経質になりすぎていただけかもしれません

enumについては次回から使うようにします。

>GRAMさん
そうですね
大きくなって困ってるんだったらクラス分けろってことですよね


みなさんありがとうございました。
とりあえずクラス変数を使うとこにします。
で、Item関係の処理はまとめられそうなので内部クラスを使うかどうか検討してみて
使えそうなら頑張って使っていこうと思います。

閉鎖

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