C++ шаблонная реализация многотонного шаблона
Я реализовал многотонный шаблон, используя шаблонный класс в 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();
Есть предложения по улучшению?
3 ответа
1) Персональные настройки, но я бы поменял порядок параметров шаблона и использовал бы по умолчанию Key в 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;
};
Это обо всем, что я могу думать сейчас.
Одним из улучшений было бы переписать getRef
использовать getPtr
(или наоборот, направление не так важно, как не повторяться):
static T& getRef(const Key& key)
{
return *getPtr(key);
}
Кажется, вы проделали очень впечатляющую работу.
Кстати, могу я спросить, почему вы привели экземпляр (в стиле c) здесь?
return (T*)(it->second);
Я думаю, было бы чище, если бы вы просто написали
return it->second;
Кроме того, поскольку этому посту уже 10 лет, я немного изменил версию в современном стиле С++, используя интеллектуальный указатель. Пожалуйста, взгляните!
мультитонн.ч
#ifndef MULTITON_H
#define MULTITON_H
#include <map>
#include <string>
template <typename T, typename Key = int> class Multiton {
public:
static void DestroyAll();
static void Destroy(const Key &key);
static std::shared_ptr<T> GetPtr(const Key &key);
static T &GetRef(const Key &key) { return *GetPtr(key); }
protected:
Multiton();
~Multiton();
private:
Multiton(const Multiton &) = default;
Multiton &operator=(const Multiton &) = default;
static std::map<Key, std::shared_ptr<T>> instances_;
};
#endif // MULTITON_H
multiton.cpp
#include "multiton.h"
template <typename T, typename Key> void Multiton<T, Key>::DestroyAll() {
for (auto it = instances_.begin(); it != instances_.end(); ++it)
delete (*it).second;
instances_.clear();
}
template <typename T, typename Key> void Multiton<T, Key>::Destroy(const Key &key) {
auto it = instances_.find(key);
if (it != instances_.end()) {
delete (*it).second;
instances_.erase(it);
}
}
template <typename T, typename Key> std::shared_ptr<T> Multiton<T, Key>::GetPtr(const Key &key) {
const auto it = instances_.find(key);
if (it != instances_.end())
return (it->second);
std::shared_ptr<T> instance = std::make_shared<T>();
instances_[key] = instance;
return instance;
}
template <typename T, typename Key> Multiton<T, Key>::Multiton() {}
template <typename T, typename Key> Multiton<T, Key>::~Multiton() {}
template <typename T, typename Key> std::map<Key, std::shared_ptr<T>> Multiton<T, Key>::instances_;