前回のソースはインターフェースを使っている例なんですが、実は C++ の場合はメンバ関数のポインタを使えるのでインタフェースは必要ありません…と言いますから、インタフェースの代わりになります。Java だと定番のリスナーパターンというとでインターフェースを利用、C# の場合はデリゲートを使います。このあたり、リスナーパターンは言語によって違った実装ができるという訳で。
メンバ関数を使って書き直したのが次のコードです。
// for 誰かが使ったらアリスに知らせろ
#include <iostream>
#include <list>
using namespace std;
class Person ;
class INotify {
protected:
Person *person ;
// メンバ関数ポインタ
void (Person::*onTatch)();
public:
// メンバ関数の登録
void setCatcher(Person *per, void (Person::*catcher)()) {
person = per;
onTatch = catcher;
}
// 呼出し
virtual void OnTatch( void *bag ) {
if ( onTatch != NULL ) {
(person->*onTatch)();
}
}
};
class Bag : public INotify {
protected:
void *_mark;
public:
Bag() : _mark(NULL) {}
virtual void setMark( void *mark ) {
// 1回だけマークできる
if ( _mark == NULL ) {
_mark = mark;
}
}
virtual void *getMark() {
return _mark;
}
virtual void Used( void *user ) {
if ( _mark != NULL && _mark != user ) {
OnTatch( this );
}
}
};
class Prada : public Bag {};
class Tiffany : public Bag {};
class Person
{
private:
list<Bag*> bags;
public:
Person() {
}
void Present( Bag *bag )
{
if ( dynamic_cast<Prada*>(bag) != NULL &&
bag->getMark() == NULL )
{
bag->setMark( this );
// メンバ関数の登録
bag->setCatcher( this, &Person::onTatch );
bags.push_back( bag );
}
}
int CountBags()
{
return bags.size();
}
void Use( Bag *bag ) {
bag->Used( this );
}
void onTatch() {
cout << "who use my Prada bag!!!" << endl;
}
};
int main(void)
{
Person alice;
Person lolita;
Prada *bag = new Prada();
// アリスにプレゼント
alice.Present( bag );
// アリスが使う分には問題なし
alice.Use( bag );
// 同じ鞄をロリータにプレゼント
lolita.Present( bag );
// ロリータが鞄を使うと...
lolita.Use( bag );
return 0;
}
これも実行すると、次のように誰が使ったんじゃッ!!!ってことになります。
D:\work\blog\src\alice>a who use my Prada bag!!!
関数ポインタ、メンバ関数ポインタの書き方はちょっと難しくてコンパイルエラーとの格闘になります。慣れると関数部分をポインタにするだけだなぁ、慣れない場合はちょっと意味不明な感じがしますね。配列の配列のポインタなんかと一緒です。
