メンバ関数の関数ポインタ

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

メンバ関数の関数ポインタ

#1

投稿記事 by チルチル » 16年前

void(*Menu[/url])(void)={
Title,
Protocol,
Scene.Number,
Loop.End,
};
Menu();

C++で上のような感じの関数ポインタを作りたいのですが
関数のポインタとメンバ関数のポインタが混在していたり
メンバ関数のポインタだけでもエラーが出ます
書き方が間違っているのでしょうか?

環境は「Microsoft Visual C++ 2008 Express Edition」です

MNS

Re:メンバ関数の関数ポインタ

#2

投稿記事 by MNS » 16年前

どの関数がメンバ関数でしょうか?

メンバ関数のポインタを取得するには、
そのメンバ関数をクラス名で修飾し、&演算子を使う必要があります。
また、関数ポインタはいわゆる「メンバ関数ポインタ」というものとなり、
クラスの参照が必要となります。

例:
class TestClass
{
public:
	void func(){}
};

int main()
{
	void (TestClass::*pfunc)() = &TestClass::func;
	//以下のようにして呼び出す
	TestClass tc;
	(tc.*pfunc)();
}
メンバ関数が静的メンバならば、
void (*pfunc)() = &TestClass::func;
このように、通常の関数ポインタとしても利用できます。

チルチル

Re:メンバ関数の関数ポインタ

#3

投稿記事 by チルチル » 16年前

void(*Menu[/url])(void)={
Title,//Title関数
Protocol,//Protocol関数
Scene.Number,//SceneクラスのNumber関数
Loop.End,//LoopクラスのEnd関数
};
見たいな感じです

使用する時にインスタンス名を書く必要があるんですね・・
それだとMenu();のように書けないから
配列にする意味が無いですね・・

予定ではオブジェクト1つにつきインスタンスが複数作成される事は無いので
メンバ関数が静的でも問題は無いと思いますが

End関数にstaticを付けると
「静的でないメンバ 'Loop::flag' への参照が正しくありません。」と出ます

End関数の中身は
static void End(void){
	flag=0;
}

となっているのですが
メンバ変数flagが問題なんでしょうか・・

zwi

Re:メンバ関数の関数ポインタ

#4

投稿記事 by zwi » 16年前

静的メンバは、静的メンバ変数しかアクセスできません。
なのでflagもstaticである必要があります。
しかし、ここまでくるとクラス化する意義があるかは疑問ですね。

MNS

Re:メンバ関数の関数ポインタ

#5

投稿記事 by MNS » 16年前

カプセル化の原則より、静的メンバ関数が静的でないメンバにアクセスすることは出来ません。
flagを静的メンバにすればアクセスすることは出来ます。

チルチルさんが何を行おうとしているのかは私には分からないので、あまりはっきりしたことはいえませんが、
一回クラスの設計を見直してみてはいかがでしょう。関数ポインタ配列で一まとめにしようとしているところをみると、
その関数の間に何らかの繋がりがあるはずです。
一回、それらの関数を一つのクラスでまとめることも考えてみてはいかかでしょうか?

チルチル

Re:メンバ関数の関数ポインタ

#6

投稿記事 by チルチル » 16年前

複数宣言しないのにクラスを使うのもアレですが
関数だと無駄が多すぎるんですよね・・
flagをstaticにしてみると
「外部シンボル ""private: static bool Loop::flag" (?flag@Loop@@0_NA)" は未解決です。」
「外部参照 1 が未解決です。」と出てしまいます・・

チルチル

Re:メンバ関数の関数ポインタ

#7

投稿記事 by チルチル » 16年前

何をやろうとしているかと言うと
メインループでswitch文を使って処理を分けるのを
関数ポインタで実現しようとしています
その処理は関数であったりメンバ関数であったりします
1つのクラスにまとめると結局switch文を使う事になるので無理です

zwi

Re:メンバ関数の関数ポインタ

#8

投稿記事 by zwi » 16年前

あっ、すいません言い忘れてました。
flagはprivateではなく、publicにしてください。

チルチル

Re:メンバ関数の関数ポインタ

#9

投稿記事 by チルチル » 16年前

public:

static bool flag;

としてみましたが特に変化は無いですね・・

bool Loop::flag;

を後に追加すると一応動いたんですが
これからクラスが増えていくので
クラス宣言の外側に何か書くと
物凄く見栄えが悪くなって
管理しづらいので気が進まないですね・・
なんとかならないでしょうか?

御津凪

Re:メンバ関数の関数ポインタ

#10

投稿記事 by 御津凪 » 16年前

要望に答えられそうなコードを用意してみたら、なんだかすごいことになってしまいました。
#include <stdio.h>

// 関数バインドクラス群
class FuncBind {
    typedef void (FuncBind::*CallFuncType)( void );
protected:
    template<class T>
    FuncBind( void (T::*func)( void ) ):CallFuncPtr(reinterpret_cast<CallFuncType>(func)){ }
public:
    void operator()( void ){
        (this->*CallFuncPtr)();
    }
protected:
    void*    FuncPtr;
    void*    ClassPtr;
private:
    CallFuncType    CallFuncPtr;
};
class GlobalFuncBind : public FuncBind {
public:
    GlobalFuncBind( void (*f)( void ) ):FuncBind(&GlobalFuncBind::CallFunc){ FuncPtr = f; }
    void CallFunc( void ){
        ((void (*)( void ))FuncPtr)();
    }
};
template<class T>
class ClassFuncBind : public FuncBind {
    typedef void (T::*FuncType)( void );
public:
    ClassFuncBind( T* cls, FuncType f ):FuncBind(&ClassFuncBind::CallFunc){
        void* p;
        __asm { // C++ 上では FuncType から void* に代入できないのでアセンブラで強引に
            mov eax, f
            mov p, eax
        }
        FuncPtr = p;
        ClassPtr = cls;
    }
    void CallFunc( void ){
        FuncType func;
        void* p = FuncPtr;
        __asm { // C++ 上では void* から FuncType に代入できないのでアセンブラで強引に
            mov eax, p
            mov func, eax
        }
        (((T*)ClassPtr)->*func)();
    }
};

// 呼び出しクラス
class Scene {
public:
    void Number( void ){ printf("Scene::Number\n"); }
};
class Loop {
public:
    void End( void ){ printf("Loop::End\n"); }
};

// 呼び出しグローバル関数
void Title( void ){ printf("Title\n"); }
void Protocol( void ){ printf("Protocol\n"); }

// 静的変数群
static Scene sScene;
static Loop sLoop;

static FuncBind MenuFuncs[/url] = {
    GlobalFuncBind(Title),
    GlobalFuncBind(Protocol),
    ClassFuncBind<Scene>(&sScene, &Scene::Number),
    ClassFuncBind<Loop>(&sLoop, &Loop::End),
};

// メイン関数
int main( int argc, char* argv[/url] ){
    for(int i = 0, n = sizeof(MenuFuncs) / sizeof(MenuFuncs[0]);i < n;++i){
        MenuFuncs();
    }
    getchar();
    return 0;
}

上記のコードはそのままコピペで動くはずです。(VC++でしか動かないかも)

MNS

Re:メンバ関数の関数ポインタ

#11

投稿記事 by MNS » 16年前

そのようなことをしたければ、関数ポインタ以外を使うのも手です。
オブジェクト指向プログラミングの機能の一つとして、多態性というものがあります。
詳しく話すと非常に長くなってしまい、説明は出来ないのですが、
こういうものである、というプログラムの例を示します。
#include <iostream>

class State
{
public:
	virtual void Update() = 0;
};

class Title :public State
{
public:
	void Update()
	{
		std::cout << "タイトル\n";
	}
};

class Menu :public State
{
public:
	void Update()
	{
		std::cout << "メニュー\n";
	}
};

class End :public State
{
public:
	void Update()
	{
		std::cout << "エンド\n";
	}
};

int main()
{
	State* state[3];
	state[0] = new Title();
	state[1] = new Menu();
	state[2] = new End();

	int s = 0;

	state->Update();
	++s;
	state->Update();
	++s;
	state->Update();

	for(int i = 0; i < 3; ++i)
	{
		delete state;
	}
};

チルチル

Re:メンバ関数の関数ポインタ

#12

投稿記事 by チルチル » 16年前

コードを作って頂いてありがとうございます
しかし外部宣言するより管理しづらいですね・・
少し考えます、ちょっと待って下さい

Justy

Re:メンバ関数の関数ポインタ

#13

投稿記事 by Justy » 16年前

 VisualStudio2008 SP1なら
[color=#d0d0ff" face="monospace]
#include <functional>

typedef std::tr1::function<void ()> MenuFuncType;
const MenuFuncType Menu[/url]=
{
Title,
Protocol,
std::tr1::bind(&SceneClass::Number, &Scene),
std::tr1::bind(&LoopClass::End, &Loop),
};

int main()
{
for(int n=0; n < sizeof(Menu)/sizeof(Menu[0]); ++n)
Menu[n]();
return 0;
}
[/color]

で、関数ポインタとメンバ関数ポインタを混在させた配列を作ることが出来ますが、
設計的にそういう状況そのものがかなりどうかと。



# 御津凪さん

>上記のコードはそのままコピペで動くはずです

 そのコードのままでしたら大丈夫かもしれませんが、
ClassFuncBindで sizeof(FuncType)が sizeof(void*)より
大きくなっても大丈夫ですか?

チルチル

Re:メンバ関数の関数ポインタ

#14

投稿記事 by チルチル » 16年前

<functional>と言うライブラリは知りませんでしたね・・
自分で実装できるか試してみます
少々お待ちください・・

御津凪

Re:メンバ関数の関数ポインタ

#15

投稿記事 by 御津凪 » 16年前

> Justy さん
> ClassFuncBindで sizeof(FuncType)が sizeof(void*)より
> 大きくなっても大丈夫ですか?

そこは想定していないので、「動く"はず"」と書きました。
そもそも一から STL も使わずに書いたらああなったので。

そういう意味では Justy さんの方法がコード的に簡潔かつ安全です。

nayo

Re:メンバ関数の関数ポインタ

#16

投稿記事 by nayo » 16年前

こちらのようなものを使ってみてはどうでしょうか
http://marupeke296.com/DXCLS_MethodExecTemplate.html

ゲーム管理クラスのようなものを用意する必要がありますが

Justy

Re:メンバ関数の関数ポインタ

#17

投稿記事 by Justy » 16年前


>そこは想定していないので、「動く"はず"」と書きました。
>そもそも一から STL も使わずに書いたらああなったので。

 なるほど、失礼しました。

チルチル

Re:メンバ関数の関数ポインタ

#18

投稿記事 by チルチル » 16年前

>こちらのようなものを使ってみてはどうでしょうか
読んで見ましたが理解できませんでした・・

><functional>
一応動きましたがオブジェクトとインスタンスが同名だとエラーになりますね・・
「std::tr1::」のあたりがよくわからないです

conio

Re:メンバ関数の関数ポインタ

#19

投稿記事 by conio » 16年前

こんな感じで、クラス内状態遷移ができれば良いのでしょうか?
(下記のは、単純にメンバ関数を呼び出してるだけですが)
--------------------------------------------------------------------------
#include<stdio.h>
class Test{
public:
	Test();
	~Test();
	void Top(void);
private:
	typedef void(Test::*Func)();
	Func fc[7];

	void FuncA(void);
	void FuncB(void);
	void FuncC(void);
	void FuncD(void);
	void FuncE(void);
	void FuncF(void);
	void FuncG(void);
};

Test::Test()
{
	fc[0] = &Test::FuncA;
	fc[1] = &Test::FuncB;
	fc[2] = &Test::FuncC;
	fc[3] = &Test::FuncD;
	fc[4] = &Test::FuncE;
	fc[5] = &Test::FuncF;
	fc[6] = &Test::FuncG;
}

Test::~Test()
{

}

void Test::Top(void)
{
	for(int i = 0; i < 7; i++)
		(this->*fc)();
}

void Test::FuncA(void)
{
	printf("お茶\n");
}

void Test::FuncB(void)
{
	printf("ほうじ茶\n");
}

void Test::FuncC(void)
{
	printf("はと麦茶\n");
}

void Test::FuncD(void)
{
	printf("プーアール茶\n");
}

void Test::FuncE(void)
{
	printf("シルベスタギムネマ茶\n");
}

void Test::FuncF(void)
{
	printf("梅こぶ茶\n");
}

void Test::FuncG(void)
{
	printf("玉露\n");
}

int main(void)
{
	Test test;
	test.Top();

	return(0);
}
--------------------------------------------------------------------------



【追記】
「様々な別のクラスへ遷移する」ものを作ろうとしてたのですね。
解釈を間違えてました。

それならば、基底クラスから複数の派生クラスを作り、
基底クラス型のポインタに派生クラスを入れて遷移させるようにしてみてはどうでしょう?

チルチル

Re:メンバ関数の関数ポインタ

#20

投稿記事 by チルチル » 16年前

>それならば、基底クラスから複数の派生クラスを作り

基準となるクラスを他のクラスが継承すると言う事でしょうか?
構造が全く違うようなクラスを添え字で使い分けたいので無理そうですね・・

あと普通の関数が入る事は無さそうです
なので色々なクラスのメンバ関数を集めた関数ポインタが作りたいです
戻り値と引数は全てvoidで統一しています

例えると次のような機能を関数ポインタで再現したいです
switch(i){
	case 1: A.Run(); break;
	case 2: B.Run(); break;
	case 3: C.Run(); break;
}
A,B,Cは全て別のクラスのインスタンスです
名前は同じですがRunの中身は違います
しかし戻り値と引数は全てvoidです

yu

Re:メンバ関数の関数ポインタ

#21

投稿記事 by yu » 16年前

そういうことなら
No:39680 の MNS様 の記事を参考にすると良いと思います
関数ポインタでは無いですが・・・

同じ機能を親クラスに纏めて、親クラスを派生させれば良いです

今の場合だと Runメソッド を仮想関数にして親クラスに書けばいいです

チルチル

Re:メンバ関数の関数ポインタ

#22

投稿記事 by チルチル » 16年前

う~ん使えそうではありますが
メンバ関数名が全て同じとは限らないし
継承はならべく使いたくないですね・・
それからクラスの作成と同時にインスタンス化したいので
newは少しマズイですね・・

conio

Re:メンバ関数の関数ポインタ

#23

投稿記事 by conio » 16年前

予め、どこのクラスのメンバ関数(全て同名)を実行するかを配列で定義しておくのならば、
こんな感じでどうでしょうか。

無理やり一つの.cppに纏めてしまったので、ちょっと見づらいと思います。
クラスごとに複数のヘッダファイルやソースファイルに分割すればよいかと。
-----------------------------------------------------------------------------
#include<stdio.h>
#define     SAFE_DELETE(p)  { if (p) { delete (p);     (p)=NULL; } }

class Child{
public:
	virtual void Exec( void ) = 0;
	virtual ~Child(){ }
};

class ChildC : public Child{
public:
	ChildC(){  };
	~ChildC(){  };
	void Exec( void );
};

void ChildC::Exec(void)
{
	printf("ChildC::Execです。\n");
}

class ChildB : public Child{
public:
	ChildB(){  };
	~ChildB(){  };
	void Exec( void );
};

void ChildB::Exec(void)
{
	printf("ChildB::Execです。\n");
}

class ChildA : public Child{
public:
	ChildA(){  };
	~ChildA(){  };
	void Exec( void );
};

void ChildA::Exec(void)
{
	printf("ChildA::Execです。\n");
}

class Parent{
public:
	Parent();
	~Parent();
	void Run(void);
private:
	Child* mChild[3];
	int ChildNum;
};

Parent::Parent()
{
	mChild[0] = new ChildA;
	mChild[1] = new ChildB;
	mChild[2] = new ChildC;
	ChildNum = sizeof(mChild)/sizeof(mChild[0]);
}

Parent::~Parent()
{
	for(int i = 0; i < ChildNum; i++)
		SAFE_DELETE(mChild);
}

void Parent::Run(void)
{
	for(int i = 0; i < ChildNum; i++)
		mChild->Exec();
}

int main(void)
{
	Parent p;
	p.Run();

	return(0);
}
-----------------------------------------------------------------------------

【追記】
後で気付きましたが、既にMNSさんが記述して下さってました。

MNS

Re:メンバ関数の関数ポインタ

#24

投稿記事 by MNS » 16年前

>メンバ関数名が全て同じとは限らないし
>継承はならべく使いたくないですね・・
>それからクラスの作成と同時にインスタンス化したいので

そうすると、なぜクラスを使用しているのか疑問です。
使用しなければならない何らかの理由が無いのなら、
いっそクラスを使わないのも一つの手だと思います。

チルチル

Re:メンバ関数の関数ポインタ

#25

投稿記事 by チルチル » 16年前

クラスを使う理由はコンストラクタがあるからですね
関数内で使う画像を読み込みます

関数はstaticで何とかなるかもしれませんが
起動時にローディングしてくれないので困りますね

インスタンスに&を使えばできるかもしれませんが
ローディング関係を除けば関数の方が良いので迷いますね・・

MNS

Re:メンバ関数の関数ポインタ

#26

投稿記事 by MNS » 16年前

それは、画像を読み込む関数を作成し、起動時に呼び出す、という形ではいけないのでしょうか?
それとも、クラスのカプセル化の機能を使いたいということでしょうか?

いずれにせよ、staticにした時点でクラスの非静的メンバにはアクセスできなくなりますから、
結局のところコンストラクタは意味をなさなくなるかと思います。

チルチル

Re:メンバ関数の関数ポインタ

#27

投稿記事 by チルチル » 16年前

クラスのカプセル化が目的ですね

なので一番近いのは仮想関数を継承する事みたいです
画像を必要としないクラスも存在しますが
一貫性を上げるためにはアリだと思います

なので仮想関数の継承を試して見ます

皆さんが今まで出してくれた方法でも実現できるんですが
実装するとコードの見栄えが悪くなるので模索中です・・

チルチル

Re:メンバ関数の関数ポインタ

#28

投稿記事 by チルチル » 16年前

仮想関数の継承でうまく行きました
皆さんありがとうございます

閉鎖

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