singletonについて
ググるとほとんどがCRTPを使用したものだった。
と、言うわけで、継承を使用せずに汎用的なsingletonを実現できないのかなぁと思って実際に作ってみた。
template <typename T> class singleton : boost::noncopyable { singleton () {} ~singleton () {} public: static T& get_instance () { static T t; return t; } };
※訂正 2011/1/4 「struct singleton」じゃなくて「class singleton」デスヨネー
CRTPとの違いはコンストラクタとデストラクタがprivateな所だけ。
C++03用ならマルチスレッドに対応するため、適切にロックを掛ける必要があるけども気にしない気にしない。
使い方は簡単。
class tonimaru_ : boost::noncopyable { friend class singleton<tonimaru_>; tonimaru_ () {} ~tonimaru_ () {} public: void print () { std::cout << "ようじょっ!\n"; } }; typedef singleton<tonimaru_> tonimaru; int main() { tonimaru::get_instance().print(); }
出力結果:
ようじょっ!
完成。
と言っても、これじゃあ1個しか作れない。
たとえば、singletonの構造で最大5個まで作りたい時もあるはず。
そんなのに対応してみる。
template <typename T, size_t N = 1> class singleton : boost::noncopyable { singleton () {} ~singleton () {} public: template <size_t I> static T& get_instance () { BOOST_STATIC_ASSERT(I < N); return get_instance_(I); } private: static T& get_instance_ (size_t i) { static T t[N]; return t[i]; } }; template <typename T> class singleton<T, 1u> : boost::noncopyable { singleton () {} ~singleton () {} public: static T& get_instance () { static T t; return t; } };
たぶん、これで大丈夫。
さっそく使ってみる。
class tonimaru_ : boost::noncopyable { friend class singleton<tonimaru_>; friend class singleton<tonimaru_, 2u>; tonimaru_ () {} ~tonimaru_ () {} public: void print () { std::cout << "ようじょっ!\n"; } }; typedef singleton<tonimaru_> tonimaru; typedef singleton<tonimaru_, 2> tonimaru_2;
これで定義は完了。
早速呼んでみる。
int main () { tonimaru::get_instance().print(); tonimaru_2::get_instance<0>().print(); tonimaru_2::get_instance<1>().print(); tonimaru_2::get_instance<2>().print(); // えらー }
うん、できた。
なんかびみょーだけど。
配列のサイズが大きいと最初の生成のコストが半端なかったり。
配列のサイズをクライアントが1に指定すると通常のsingletonになっちゃって若干直観的じゃない気もする。
まぁ今はこんなもんか。
※もちろん、なるべくsingletonを使用しない設計が大事ってのが大前提ですにゃ。