2010-02-27 3 views
2

Я реализовал многотонный шаблон с использованием шаблонного класса на C++.C++ templated реализация класса многотонового шаблона

#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(); 

Любые предложения по улучшению?

+0

Интересно. Я не знал, что есть шаблон «Multiton», когда я написал этот ответ: http://stackoverflow.com/questions/363453/looking-for-a-better-c-class-factory/363543#363543. Я использовал этот термин для совершенно другого значения :) –

ответ

1

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

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

1) Личные предпочтения, но я бы в обратном порядке параметры шаблона и по умолчанию ключа к StD :: строки (если это то, что вы будете использовать большинство)

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

Тогда вы можете сделать это:

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

Что я думаю, это лучше.

2) Также, если вы никогда не проезжаете указатели/ссылки на Multitron (что бы не нарушало действие patter), вам не нужен виртуальный деструктор в классе.

3) Если вы использовали более умный контейнер для своих T * s, вы могли бы избежать вызова Foo :: destroy(). Что-то вроде std::map<Key,boost::shared_ptr<T> > уничтожит все объекты, когда статический экземпляр был уничтожен. (Хотя, если вы заботитесь о порядке уничтожения, то вам нужно что-то умнее - вы могли бы приспособить somethign из существующих решений синглтона, таких как синглтоны phoenix и т. Д.)

4) Вы можете изменить итераторы на const_iterators.

5) уничтожить, вероятно, следует очистить карту, чтобы предотвратить случайный доступ к недействительной памяти после вызова destroy. Или, если вы хотите защитить от этого, вы должны выбросить исключение.

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

6) Если вы не используете полиморфный T, то вы можете использовать зЬй :: карта и ваш код будет выглядеть следующим образом ...

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

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

+0

О вашей точке 6 - можете ли вы подробно остановиться на точном условии использования этого упрощенного кода? Где нелимиморфизм Т вступает в игру? – einpoklum

+0

Если вы поместите объект типа 'D', полученный из' T' в карту, он будет нарезан обратно в базовый класс T - аналогично тому, что вызывается 'T temp = d', - и компилятор, вероятно, даже не скажет вам , На самом деле, если T - абстрактный класс, вы даже не сможете создать карту, поэтому все это не будет компилироваться. –

Смежные вопросы