ページ 1 / 1
メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 16:04
by チルチル
void(*Menu[/url])(void)={
Title,
Protocol,
Scene.Number,
Loop.End,
};
Menu();
C++で上のような感じの関数ポインタを作りたいのですが
関数のポインタとメンバ関数のポインタが混在していたり
メンバ関数のポインタだけでもエラーが出ます
書き方が間違っているのでしょうか?
環境は「Microsoft Visual C++ 2008 Express Edition」です
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 16:15
by MNS
どの関数がメンバ関数でしょうか?
メンバ関数のポインタを取得するには、
そのメンバ関数をクラス名で修飾し、&演算子を使う必要があります。
また、関数ポインタはいわゆる「メンバ関数ポインタ」というものとなり、
クラスの参照が必要となります。
例:
class TestClass
{
public:
void func(){}
};
int main()
{
void (TestClass::*pfunc)() = &TestClass::func;
//以下のようにして呼び出す
TestClass tc;
(tc.*pfunc)();
}
メンバ関数が静的メンバならば、
void (*pfunc)() = &TestClass::func;
このように、通常の関数ポインタとしても利用できます。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 17:04
by チルチル
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が問題なんでしょうか・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 17:17
by zwi
静的メンバは、静的メンバ変数しかアクセスできません。
なのでflagもstaticである必要があります。
しかし、ここまでくるとクラス化する意義があるかは疑問ですね。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 17:20
by MNS
カプセル化の原則より、静的メンバ関数が静的でないメンバにアクセスすることは出来ません。
flagを静的メンバにすればアクセスすることは出来ます。
チルチルさんが何を行おうとしているのかは私には分からないので、あまりはっきりしたことはいえませんが、
一回クラスの設計を見直してみてはいかがでしょう。関数ポインタ配列で一まとめにしようとしているところをみると、
その関数の間に何らかの繋がりがあるはずです。
一回、それらの関数を一つのクラスでまとめることも考えてみてはいかかでしょうか?
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 17:26
by チルチル
複数宣言しないのにクラスを使うのもアレですが
関数だと無駄が多すぎるんですよね・・
flagをstaticにしてみると
「外部シンボル ""private: static bool Loop::flag" (?flag@Loop@@0_NA)" は未解決です。」
「外部参照 1 が未解決です。」と出てしまいます・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 17:31
by チルチル
何をやろうとしているかと言うと
メインループでswitch文を使って処理を分けるのを
関数ポインタで実現しようとしています
その処理は関数であったりメンバ関数であったりします
1つのクラスにまとめると結局switch文を使う事になるので無理です
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 18:03
by zwi
あっ、すいません言い忘れてました。
flagはprivateではなく、publicにしてください。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 18:21
by チルチル
public:
static bool flag;
としてみましたが特に変化は無いですね・・
bool Loop::flag;
を後に追加すると一応動いたんですが
これからクラスが増えていくので
クラス宣言の外側に何か書くと
物凄く見栄えが悪くなって
管理しづらいので気が進まないですね・・
なんとかならないでしょうか?
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 19:08
by 御津凪
要望に答えられそうなコードを用意してみたら、なんだかすごいことになってしまいました。
#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++でしか動かないかも)
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 19:12
by MNS
そのようなことをしたければ、関数ポインタ以外を使うのも手です。
オブジェクト指向プログラミングの機能の一つとして、
多態性というものがあります。
詳しく話すと非常に長くなってしまい、説明は出来ないのですが、
こういうものである、というプログラムの例を示します。
#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:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 19:14
by チルチル
コードを作って頂いてありがとうございます
しかし外部宣言するより管理しづらいですね・・
少し考えます、ちょっと待って下さい
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 19:43
by Justy
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:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 20:04
by チルチル
<functional>と言うライブラリは知りませんでしたね・・
自分で実装できるか試してみます
少々お待ちください・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 20:11
by 御津凪
> Justy さん
> ClassFuncBindで sizeof(FuncType)が sizeof(void*)より
> 大きくなっても大丈夫ですか?
そこは想定していないので、「動く"はず"」と書きました。
そもそも一から STL も使わずに書いたらああなったので。
そういう意味では Justy さんの方法がコード的に簡潔かつ安全です。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 20:18
by nayo
こちらのようなものを使ってみてはどうでしょうか
http://marupeke296.com/DXCLS_MethodExecTemplate.html
ゲーム管理クラスのようなものを用意する必要がありますが
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 20:26
by Justy
>そこは想定していないので、「動く"はず"」と書きました。
>そもそも一から STL も使わずに書いたらああなったので。
なるほど、失礼しました。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 20:46
by チルチル
>こちらのようなものを使ってみてはどうでしょうか
読んで見ましたが理解できませんでした・・
><functional>
一応動きましたがオブジェクトとインスタンスが同名だとエラーになりますね・・
「std::tr1::」のあたりがよくわからないです
Re:メンバ関数の関数ポインタ
Posted: 2009年9月27日(日) 23:11
by conio
こんな感じで、クラス内状態遷移ができれば良いのでしょうか?
(下記のは、単純にメンバ関数を呼び出してるだけですが)
--------------------------------------------------------------------------
#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:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 00:22
by チルチル
>それならば、基底クラスから複数の派生クラスを作り
基準となるクラスを他のクラスが継承すると言う事でしょうか?
構造が全く違うようなクラスを添え字で使い分けたいので無理そうですね・・
あと普通の関数が入る事は無さそうです
なので色々なクラスのメンバ関数を集めた関数ポインタが作りたいです
戻り値と引数は全てvoidで統一しています
例えると次のような機能を関数ポインタで再現したいです
switch(i){
case 1: A.Run(); break;
case 2: B.Run(); break;
case 3: C.Run(); break;
}
A,B,Cは全て別のクラスのインスタンスです
名前は同じですがRunの中身は違います
しかし戻り値と引数は全てvoidです
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 00:34
by yu
そういうことなら
No:39680 の MNS様 の記事を参考にすると良いと思います
関数ポインタでは無いですが・・・
同じ機能を親クラスに纏めて、親クラスを派生させれば良いです
今の場合だと Runメソッド を仮想関数にして親クラスに書けばいいです
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 00:45
by チルチル
う~ん使えそうではありますが
メンバ関数名が全て同じとは限らないし
継承はならべく使いたくないですね・・
それからクラスの作成と同時にインスタンス化したいので
newは少しマズイですね・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 01:13
by conio
予め、どこのクラスのメンバ関数(全て同名)を実行するかを配列で定義しておくのならば、
こんな感じでどうでしょうか。
無理やり一つの.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さんが記述して下さってました。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 06:58
by MNS
>メンバ関数名が全て同じとは限らないし
>継承はならべく使いたくないですね・・
>それからクラスの作成と同時にインスタンス化したいので
そうすると、なぜクラスを使用しているのか疑問です。
使用しなければならない何らかの理由が無いのなら、
いっそクラスを使わないのも一つの手だと思います。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 17:25
by チルチル
クラスを使う理由はコンストラクタがあるからですね
関数内で使う画像を読み込みます
関数はstaticで何とかなるかもしれませんが
起動時にローディングしてくれないので困りますね
インスタンスに&を使えばできるかもしれませんが
ローディング関係を除けば関数の方が良いので迷いますね・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 17:50
by MNS
それは、画像を読み込む関数を作成し、起動時に呼び出す、という形ではいけないのでしょうか?
それとも、クラスのカプセル化の機能を使いたいということでしょうか?
いずれにせよ、staticにした時点でクラスの非静的メンバにはアクセスできなくなりますから、
結局のところコンストラクタは意味をなさなくなるかと思います。
Re:メンバ関数の関数ポインタ
Posted: 2009年9月28日(月) 18:01
by チルチル
クラスのカプセル化が目的ですね
なので一番近いのは仮想関数を継承する事みたいです
画像を必要としないクラスも存在しますが
一貫性を上げるためにはアリだと思います
なので仮想関数の継承を試して見ます
皆さんが今まで出してくれた方法でも実現できるんですが
実装するとコードの見栄えが悪くなるので模索中です・・
Re:メンバ関数の関数ポインタ
Posted: 2009年9月29日(火) 21:38
by チルチル
仮想関数の継承でうまく行きました
皆さんありがとうございます