「template × mix-in」の編集履歴(バックアップ)一覧に戻る
template × mix-in - (2010/12/28 (火) 17:10:37) の編集履歴(バックアップ)
template と mix-in
last update: 2010/12/28 (Tue)
Mix-in とは,継承を用いて既存のクラスに機能を後付けしたいときに使う.簡単なコードは以下の通り.
// mixin.h #include <list> // for std::list #include <algorithm> // for std::find // コンテナに格納されるクラス class A { /* 省略 */ } // コンテナ機能を付加する Mix-in class MixIn { std::list<A> as; public: MixIn() {} virtual ~MixIn() {} void add(A* a); void remove(A* a); }; // Mix-in されるクラス class Container : public MixIn { public: Container() : MixIn() {} ~Container() {} }; // mixin.cpp #include "mixin.h" // MixIn の実装 void MixIn::add(A* a) { as.push_back(a); } void MixIn::remove(A* a); { typedef std::list<A>::iterator iter; iter i = std::find(as.begin(), as.end(), a); if (i != as.end()) as.erase(i++); }
コンテナのデストラクタの実装はサボり^^.こんな感じで,Container クラスに後付けでクラス A のコンテナ機能を付加できる.
Mix-in が便利なのは,複数の Mix-in を用意して,それをがんがん後付けしていけること.試しにクラス B のコンテナ機能も付加してみる.
その1
// mixin.h #include <list> // for std::list #include <algorithm> // for std::find // コンテナに格納されるクラス class A { /* 省略 */ } class B { /* 省略 */ } // A のコンテナ機能を付加する Mix-in class MixInA { std::list<A> as; public: MixInA() {} virtual ~MixInA() {} void add(A* a); void remove(A* a); }; // B のコンテナ機能を付加する Mix-in class MixInB { std::list<B> bs; public: MixInB() {} virtual ~MixInB() {} void add(B* b); void remove(B* b); }; // Mix-in されるクラス class Container : public MixInA, public MixInB { public: Container() : MixInA(), MixInB() {} ~Container() {} }; // mixin.cpp #include "mixin.h" // MixInA の実装 void MixInA::add(A* a) { as.push_back(a); } void MixInA::remove(A* a); { typedef std::list<A>::iterator iter; iter i = std::find(as.begin(), as.end(), a); if (i != as.end()) as.erase(i++); } // MixInB の実装 void MixInB::add(B* b) { bs.push_back(b); } void MixInB::remove(B* b); { typedef std::list<B>::iterator iter; iter i = std::find(bs.begin(), bs.end(), b); if (i != bs.end()) bs.erase(i++); }
main 関数から Cointainer::add を呼んでやって,これを gcc でコンパイルすると
error: request for member add is ambiguous
と怒られる. MixInA から継承した add(A* a) と MixInB から継承した add(B* a) では引数の型が違うのにもかかわらず,自動ではオーバーロードされない.これは template を用いた mix-in でも同じで,最後に Mix-in したメソッド以外は名前修飾してやらないとメソッドが見つからないというエラーになる.
pImpl と継承? の(その2)でもメソッドのオーバーライドをしているが,やはり C++ で実装の再利用に public 継承を用いてはいけないのである(たぶん). C++ の教義に従えば,ここは Mix-in を private 継承し, Container 内でそれを呼ぶのがスマートである(ような気がする).
その2
// mixin.h #include <list> // for std::list #include <algorithm> // for std::find // コンテナに格納されるクラス class A { /* 省略 */ } class B { /* 省略 */ } // A のコンテナ機能を付加する Mix-in class MixInA { std::list<A> as; public: MixInA() {} ~MixInA() {} void add(A* a); void remove(A* a); }; // B のコンテナ機能を付加する Mix-in class MixInB { std::list<B> bs; public: MixInB() {} ~MixInB() {} void add(B* b); void remove(B* b); }; // Mix-in されるクラス class Container : private MixInA, private MixInB { public: Container() : MixInA(), MixInB() {} ~Container() {} // MixInA の呼び出し void add(A* a) { MixInA::add(a); } void remove(A* a) { MixInA::remove(a); } // MixInB の呼び出し void add(B* b) { MixInB::add(b); } void remove(B* a) { MixInB::remove(b); } }; // mixin.cpp #include "mixin.h" // MixInA の実装 void MixInA::add(A* a) { as.push_back(a); } void MixInA::remove(A* a); { typedef std::list<A>::iterator iter; iter i = std::find(as.begin(), as.end(), a); if (i != as.end()) as.erase(i++); } // MixInB の実装 void MixInB::add(B* b) { bs.push_back(b); } void MixInB::remove(B* b); { typedef std::list<B>::iterator iter; iter i = std::find(bs.begin(), bs.end(), b); if (i != bs.end()) bs.erase(i++); }
Mix-in クラスはもはや public 継承されないので,デストラクタの virtual は外している.
しかし面倒な.なんとかならんものか...
&trackback()