C ++ шаблонная реализация многотонного шаблона - PullRequest
2 голосов
/ 27 февраля 2010

Я реализовал многотонный шаблон, используя шаблонный класс в 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();

Есть предложения по улучшению?

Ответы [ 2 ]

3 голосов
/ 27 февраля 2010

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) Уничтожить, вероятно, следует очистить карту, чтобы предотвратить случайный доступ к недопустимой памяти после вызова уничтожения. Или, если вы хотите защититься от этого, вы должны выбросить исключение.

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;
};

Это все, что я могу думать сейчас.

1 голос
/ 27 февраля 2010

Одним из улучшений было бы переписать getRef для использования getPtr (или наоборот, направление не так важно, как без повторения ):

static T& getRef(const Key& key)
{
    return *getPtr(key);
}
...