前回のソースはインターフェースを使っている例なんですが、実は 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!!!
関数ポインタ、メンバ関数ポインタの書き方はちょっと難しくてコンパイルエラーとの格闘になります。慣れると関数部分をポインタにするだけだなぁ、慣れない場合はちょっと意味不明な感じがしますね。配列の配列のポインタなんかと一緒です。