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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 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;
}

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

1
2
D:\work\blog\src\alice>a
who use my Prada bag!!!

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

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