问题
I implemented the multiton pattern using a templated class in 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
Usage:
class Foo : public Multiton<std::string, Foo> {};
Foo& foo1 = Foo::getRef("foobar");
Foo* foo2 = Foo::getPtr("foobar");
Foo::destroy();
Any suggestions for improvements?
回答1:
1) Personal preference, but I'd reverse the order of the template parameters and default the Key to std::string (if that's what you'll use most)
template <typename Key, typename T> class Multiton { ... }
Then you can do this:
class Foo : public Multiton<Foo> {};
class Bar : public Multiton<Bar,int> {};
Which I think is nicer.
2) Also if you're never passing pointers/references to Multitron (which wouldn't kind of violate the patter) you shouldn't need a virtual destructor in the class.
3) If you used a smarter container for your T*s you could avoid having to call Foo::destroy().
Something like std::map<Key,boost::shared_ptr<T> >
would destroy all the objects when the static instance was destroyed. (Although if you cared about order of destruction, then you'd need something cleverer - you could adapt somethign from existing singleton solutions such as phoenix singletons etc)
4) You could change your iterators to const_iterators.
5) destroy should probably clear the map to prevent accidental access of invalid memory after calling destroy. Or if you want to protect against this you should throw an exception.
Foo* foo2 = Foo::getPtr("foobar");
Foo::destroy();
Foo::getPtr("foobar")->doSomething(); // BANG
6) If you're not using polymorphic T then you could use a std::map and your code would look like this...
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;
};
That's about all I can think of for now.
回答2:
One improvement would be to rewrite getRef
to use getPtr
(or vice versa, the direction doesn't matter so much as not repeating yourself):
static T& getRef(const Key& key)
{
return *getPtr(key);
}
回答3:
It seems you did very impressive job.
BTW, may I ask why you casted instance (in c-style) here?
return (T*)(it->second);
I think It would be cleaner if you just write
return it->second;
Additionally, as this post is 10-years old, I versioned-up a little bit in modern c++ way, using smart pointer. Please take a look!
multiton.h
#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_;
来源:https://stackoverflow.com/questions/2346091/c-templated-class-implementation-of-the-multiton-pattern