C/C++問題の気がするが、VC7.0でWinXPなのでここに質問です。
以下のソース例で、BクラスからAクラス(複数)のコールバックを呼んだ際に
目的のAクラスと違うインスタンスを指す時があり困っています。
ソース例では25番目を指定しますが、これが18番目になったりします。
typedef void(__stdcall *CallBack)(int) ;
class A
{
public:
A(void);
virtual ~A(void);
static void __stdcall Function(int id){ TRACE( %X , this ); }
};
class B
{
public:
B(void);
virtual ~B(void);
void Set( int id , CallBack p ){m_ID=id; m_CallBack=p}
void Execute(){ (*m_CallBack)(m_ID) ; }
protected:
int m_ID ;
CallBack m_CallBack ;
};
int main()
{
A tmpA[50] ;
B tmpB ;
tmpB.Set( 100 , tmpA[25].Function ) ;
tmpB.Execute() ;
}
すこし間違い。
int main()
{
A *tmpA = new A [50] ;
B tmpB ;
tmpB.Set( 100 , (*tmpA)[25].Function ) ;
tmpB.Execute() ;
}
static 関数の中で this 参照できるん?
> static void __stdcall Function(int id){ TRACE( %X , this ); }
staticメンバ関数がthisを参照できっこないんですけど...
ぁぅ、モロかぶり orz
すみません。
あまり関係ないかな?と省略してました(^^;
class A
{
(略)
protected:
static A *thisPointer ;
};
で
A *A::thisPointer = NULL ;
A::A(void)
{
A::thisPointer = this ;
}
void __stdcall A::Function(int id)
{
TRACE( %X , A::thisPointer );
}
です。
staticはクラスで共有されることを理解していますか?
クラスAで最後にコンストラクタが呼び出されたインスタンスへのポインタ
が,thisPointerに保持されますよ。
>A::Function(int id)
idってのがまた関連していそうだけど、、、
こんなことをやりたいのかと想像してみる。
#include <map>
#include <cstdio>
class A
{
static std::map<int, A*> m_list;
int m_id;
public:
A()
{
this->m_id = m_list.size();
m_list[this->m_id] = this;
}
int GetID() const { return this->m_id; }
void Print() const { printf(%d : %p\n, this->m_id, (void*)this); }
static A* GetPointer(const int id)
{
std::map<int, A*>::iterator it = m_list.find(id);
if (it != m_list.end())
return it->second;
return NULL;
}
};
std::map<int, A*> A::m_list;
int main()
{
A a1, a2, a3;
A* p;
printf(%p\n, &a1);
p = A::GetPointer(a1.GetID());
p->Print();
printf(%p\n, &a2);
p = A::GetPointer(a2.GetID());
p->Print();
printf(%p\n, &a3);
p = A::GetPointer(a3.GetID());
p->Print();
return 0;
}
そもそも、staticなメンバー関数はクラスインスタンス無しで呼べて
かつ、クラスのメンバー変数にアクセスできないわけですから
インスタンス毎に呼び分けても意味が無いのではないかなぁ。
typedef void(__stdcall *CallBack)(int, A*);
static void __stdcall Function(int id, A* pa);
とかしてその都度インスタンスを渡すならわからなくはないですけどね。
class B
{
public:
B(void);
virtual ~B(void);
void Set( int id , CallBack p, A* pa){m_ID=id; m_CallBack=p; m_pa=pa;}
void Execute(){ (*m_CallBack)(m_ID) ; }
protected:
int m_ID ;
A* m_pa
CallBack m_CallBack ;
};
tmpB.Set( 100 , A::Function, &tmpA[25] );
とか。
何がやりたくてこういう事をしているのかまで書くと
もっと色々レスがつくと思いますよ。
あ、削除に関してまったく考慮していなかったです。
デストラクタでリストから削除しないとだめですね。
ということでIDの振り方も問題ありです。
ごめん、ここ修正。
void Execute(){ (*m_CallBack)(m_ID, m_pa) ; }
動くかどうかの検証はしていないので参考程度に。
こうすればどうだろうか? 同じことではあるが…
class A
{
public:
void Function(int id)
{}
};
typedef void (A::*Callback)(int);
class B
{
public:
void Set(int id, Callback p, A* pa)
{
m_ID = id;
m_Callback = p;
m_pa = pa;
}
void Execute()
{
(m_pa->*m_Callback)(m_ID);
}
protected:
int m_ID;
A* m_pa;
Callback m_Callback;
};
tmpB.Set(100, &A::Function, &tmpA[25]);
レスありがとうございます。
Blue氏提示の方法に変更できるのか、ソースとにらめっこしてました。
>なにがやりたいのか。
まず質問の経緯です。
・classAと、classBは参照関係が全く無い。
・が、classBの状態変化を、特定のclassAインスタンスへ通知する必要がでてきた。
・でもclassAと、classBは結び付けたくない。
・そこでAも、Bも知ってる人(掲題ソースではmain)を新たに作って必要な関数だけを
登録。
と考え、修正してみたのですがバグにはまった次第です。
classBから直接、classAの関数呼んでまうのが簡単ですが、コールバックで実現できな
いかな。
などと欲張ったあたりが敗因?
Observerパターンでもいいと思います。
class Callback
{
public:
Callback() {}
virtual ~Callback() {}
virtual void Function(int id) = 0;
};
class A : public Callback
{
public:
A() {}
virtual ~A() {}
private:
void Function(int id) { TRACE( %X , this ); }
};
class B
{
public:
B() : m_ID(0), m_CallBack(NULL) {}
virtual ~B() {}
void Set(int id, Callback* p) { m_ID=id; m_CallBack=p; }
void Execute() { if(m_CallBack) m_CallBack->Function(m_ID); }
private:
int m_ID ;
Callback* m_CallBack ;
};
int main()
{
A tmpA[50] ;
B tmpB ;
tmpB.Set( 100 , &tmpA[25] ) ;
tmpB.Execute() ;
}
subaruさんのが一番素直かなぁ。
私もこの方法に一票。
class Bがclass Aをまったく知らないと言うのは無理があると思いますよ。
せめて親の型くらいは知っていないと呼び出せませんし。