3

C++ でテンプレート化されたクラスを使用してマルチトン パターンを実装しました。

#ifndef MULTITON_H
#define MULTITON_H

#include <map>

template <typename Key, typename T> class Multiton
{
public:    
    static void destroy()
    {
        for (typename std::map<Key, T*>::iterator it = instances.begin(); it != instances.end(); ++it) {
            delete (*it).second;
        }
    }

    static T& getRef(const Key& key)
    {
        typename std::map<Key, T*>::iterator it = instances.find(key);

        if (it != instances.end()) {
            return *(T*)(it->second);
        }

        T* instance = new T;
        instances[key] = instance;
        return *instance;
    }

    static T* getPtr(const Key& key)
    {
        typename std::map<Key, T*>::iterator it = instances.find(key);

        if (it != instances.end()) {
            return (T*)(it->second);
        }

        T* instance = new T;
        instances[key] = instance;
        return instance;
    }

protected:
    Multiton() {}
    virtual ~Multiton() {}

private:
    Multiton(const Multiton&) {}
    Multiton& operator= (const Multiton&) { return *this; }

    static std::map<Key, T*> instances;
};

template <typename Key, typename T> std::map<Key, T*> Multiton<Key, T>::instances;

#endif

使用法:

class Foo : public Multiton<std::string, Foo> {};
Foo& foo1 = Foo::getRef("foobar");
Foo* foo2 = Foo::getPtr("foobar");
Foo::destroy();

改善のための提案はありますか?

4

3 に答える 3

3

1) 個人的な好みですが、テンプレート パラメーターの順序を逆にして、キーを std::string にデフォルト設定します (それが最もよく使用される場合)。

template <typename Key, typename T> class Multiton { ... }

次に、これを行うことができます:

class Foo : public Multiton<Foo> {};
class Bar : public Multiton<Bar,int> {};

どちらがいいと思います。

2) また、ポインタ/参照を Multitron に渡さない場合 (パターンに違反することはありません)、クラスに仮想デストラクタは必要ありません。

3) T*s によりスマートなコンテナーを使用した場合、Foo::destroy() を呼び出す必要がなくなります。std::map<Key,boost::shared_ptr<T> >静的インスタンスが破棄されたときに、すべてのオブジェクトを破棄するようなものです。(ただし、破壊の順序を気にする場合は、より賢いものが必要になります-フェニックスシングルトンなどの既存のシングルトンソリューションから何かを適応させることができます)

4) イテレータを const_iterators に変更できます。

5) destroy は、destroy を呼び出した後に無効なメモリへの偶発的なアクセスを防ぐために、おそらくマップをクリアする必要があります。または、これを防ぎたい場合は、例外をスローする必要があります。

Foo* foo2 = Foo::getPtr("foobar");
Foo::destroy();
Foo::getPtr("foobar")->doSomething(); // BANG

6) ポリモーフィック T を使用していない場合は、std::map を使用でき、コードは次のようになります...

template <typename Key, typename T> class Multiton
{
public:
    //Can probably get rid of this guy as maps destructor will do the right thing 
    static void destroy()
    {
        instances.clear();
    }

    static T& getRef(const Key& key)
    {
        return instances[key];
    }

    static T* getPtr(const Key& key)
    {
        return &instances[key];
    }

protected:
    Multiton() {}
    virtual ~Multiton() {}

private:
    Multiton(const Multiton&) {}
    Multiton& operator= (const Multiton&) { return *this; }

    static std::map<Key, T> instances;
};

今のところ思いつくのはこれくらいです。

于 2010-02-27T05:06:49.343 に答える
2

改善の 1 つは、使用するように書き直すgetRefことです (またはその逆、方向は自分自身を繰り返さないgetPtrほど重要ではありません)。

static T& getRef(const Key& key)
{
    return *getPtr(key);
}
于 2010-02-27T04:41:30.330 に答える