views:

214

answers:

2

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?

A: 

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);
}
R Samuel Klatchko
+1  A: 

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 > 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.

Michael Anderson