Multiton模式的C++模板化类实现

b4qexyjb  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(118)

我用C++中的一个模板类实现了multiton模式。

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

有什么改进的建议吗?

vjrehmav

vjrehmav1#

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* 使用一个更聪明的容器,你可以避免调用Foo::destroy()。像std::map<Key,boost::shared_ptr<T> >这样的东西会在静态示例被销毁时销毁所有对象。(如果你关心破坏的顺序,那么你需要一些更聪明的东西--你可以从现有的单例解决方案中改编一些东西,比如phoenix单例等)
4)你可以将迭代器改为const_iterators。
5)destroy可能应该清除map,以防止在调用destroy后意外访问无效内存。或者如果你想防止这种情况,你应该抛出一个异常。

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

我只能想到这些了。

2skhul33

2skhul332#

一个改进是将getRef重写为getPtr(反之亦然,方向并不像not repeating yourself那么重要):

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

0sgqnhkj3#

看来你做得很出色。
顺便问一句,我可以问你为什么在这里铸造示例(在c风格)吗?

return (T*)(it->second);

我觉得你直接写会更清楚

return it->second;

此外,由于这篇文章已经有10年的历史了,我用现代c++的方式,使用智能指针,对它进行了一点版本升级。请看一下!
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_;

相关问题