誰かが使ったらアリスに知らせろ(2)

前回のソースはインターフェースを使っている例なんですが、実は C++ の場合はメンバ関数のポインタを使えるのでインタフェースは必要ありません…と言いますから、インタフェースの代わりになります。Java だと定番のリスナーパターンというとでインターフェースを利用、C# の場合はデリゲートを使います。このあたり、リスナーパターンは言語によって違った実装ができるという訳で。

メンバ関数を使って書き直したのが次のコードです。

[code lang=”csharp”]
// 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;
}
[/code]

これも実行すると、次のように誰が使ったんじゃッ!!!ってことになります。

[code lang=”php”]
D:\work\blog\src\alice>a
who use my Prada bag!!!
[/code]

関数ポインタ、メンバ関数ポインタの書き方はちょっと難しくてコンパイルエラーとの格闘になります。慣れると関数部分をポインタにするだけだなぁ、慣れない場合はちょっと意味不明な感じがしますね。配列の配列のポインタなんかと一緒です。

カテゴリー: C++ パーマリンク