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

前回のソースはインターフェースを使っている例なんですが、実は 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!!!

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

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