Template Metaprogramming(5) - Singleton

さて、そんなこんなで再び Singleton です。まず、これまでに作成した Singleton の実装の駄目な部分を挙げてみましょう。

  • インスタンスが破棄されるタイミングが不定
  • コンストラクタに引数を取るクラスに対応できない

一つ目は、 id:wata_d:20050723:1122066377 の例ではインスタンスを取得するメンバ変数で静的変数にしてインスタンスを確保しているためで、環境によっては破棄されないことすらあるそうです。自分の環境では普通にデストラクタの呼び出しも行われていたので、大丈夫だろうとは思うのですが……まぁ、しかし、「いつ終わるか」ははっきりしていないので、いつどのタイミングで終わらせたいという事が決まっている場合に不利です。

二つ目はそのままですね。やはり以前示した例では引数を取らないコンストラクタにしか対応しないため、柔軟性に欠けてしまいます。

ということで、これらの問題を解決するため、インスタンスの生成には new/delete を使用する形に変更し、またこの new/delete を行う処理を別クラスに持たせ、それ自体を「ポリシー」として扱ってみます。

//! Singleton オブジェクトを生成するためのポリシークラス
template<class T>
struct HogeCreater
{
    static T* Create()
    {
        return new T(100);
    }
    static void Destroy(T* obj)
    {
        delete obj;
    }
};

//! Createrを取り込めるようにした Singleton テンプレート
template<class T, template<class> class Creater>
class SingletonTmpl
{
public:
    typedef T* PtrT;

    static T* Instance()
    {
        if(!m_pInstance)
        {
            m_pInstance = Creater<T>::Create();
            ::atexit(SingletonTmpl::Destroy);
        }
        return m_pInstance;
    }
private:
    static void Destroy()
    {
        Creater<T>::Destroy(m_pInstance);
    }
    
    SingletonTmpl() {}
    SingletonTmpl(const SingletonTmpl&);
    SingletonTmpl& operator=(const SingletonTmpl&);

    static T* m_pInstance;
    friend T;
};
template<class T, template<class> class Creater>
typename SingletonTmpl<T, Creater>::PtrT SingletonTmpl<T, Creater>::m_pInstance;

//! 使用例
class Hoge : public SingletonTmpl<Hoge, HogeCreater>
{
public:
    void print() { std::cout << m_num << std::endl; }
private:
    explicit Hoge(int num) : m_num(num) {}
    int m_num;

    friend HogeCreater<Hoge>;
};

void sample()
{
    Hoge* obj = Hoge::Instance();
    obj->print();
}

やり方としては前回の、 Singleton テンプレートを継承することで Singleton に必要な実装をまかなうパターンと同様の実装です。が、今回は Creater というポリシークラスを導入し、コンストラクタに引数を持つクラスにも対応しています。

また、 new/delete を使用してインスタンスの構築と破棄を行うようにしているため、実装をいじれば破棄のタイミングを自由に変更することもできます (Destroy かませば消せる) 。以前紹介した Loki の Singleton はこの Destroy を呼び出すタイミングを設定する部分すらもがポリシー化されており、自由に設定することができますが、長くなりすぎるのでここではそこまで実装しませんでした。

このクラスを使用するために Creater を friend にしてやらないといけないところがネックでしょうか。コンストラクタを public にすればこれは必要無くなるんですが、なるべくならそれは避けたいですし。コンストラクタがデフォルトコンストラクタだけなら、 SingletonTmpl の中で friend 宣言すれば事足りるのですが、派生クラスの方でコンストラクタを追加しているので、 friend も派生クラス側でしてやらなければならないわけですね。

SingletonHolder より良い点は何をどうやってもインスタンスはひとつしか作られないことを保証できる点で、悪い点は Creater を複数使用したい時にめんどくさいというところでしょうか。でも、 Singleton だし、そんなパターンは無いのかな。まあ、普通に Singleton パターンを適用するなら、これでも重たいくらいだし、作業を楽にするにはこの程度で十分なのではないでしょうか。

さて、 Template Metaprogramming と題してだらだら書いてきましたが、連番をつけると意外と不要なプレッシャを受けることに気付いたので、今後はこれを取っ払って、思い付いた時に気楽に更新していこうかと思います。下手するとずっと終わらないネタだし……。

Singleton も一応は一区切り付いたので、今後はデザインパターンにでも触れていこうかと思います。 Visitor パターンに感動してちょっと熱をあげていたりするので、その辺りから。