【问题标题】:C++ templated class implementation of the multiton pattern多点模式的 C++ 模板类实现
【发布时间】:2020-12-07 13:04:31
【问题描述】:

我使用 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();

有什么改进建议吗?

【问题讨论】:

标签: c++ singleton design-patterns multiton


【解决方案1】:

1) 个人喜好,但我会颠倒模板参数的顺序并将密钥默认为 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&lt;Key,boost::shared_ptr&lt;T&gt; &gt; 之类的东西会销毁所有对象。 (虽然如果你关心破坏顺序,那么你需要一些更聪明的东西 - 你可以从现有的单例解决方案中调整一些东西,例如凤凰单例等)

4) 您可以将迭代器更改为 const_iterators。

5) destroy 应该可能会清除映射,以防止在调用 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;
};

目前我能想到的就这些了。

【讨论】:

  • 关于您的第 6 点 - 您能否详细说明使用此简化代码的确切条件? T 的非多态性在哪里发挥作用?
  • 如果你将一个派生自TD 类型的对象放入映射中,它会被切回到它的基类T——类似于调用T temp = d 所做的——编译器可能会甚至不告诉你。事实上,如果 T 是一个抽象类,你甚至无法创建地图,所以整个事情都不会编译。
【解决方案2】:

一种改进是将getRef 重写为使用getPtr(反之亦然,方向与not repeating yourself 无关紧要):

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

【讨论】:

    【解决方案3】:

    看来你的工作令人印象深刻。

    顺便说一句,我可以问一下你为什么在这里投射实例(以 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_;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2014-11-02
      • 1970-01-01
      • 2013-09-24
      相关资源
      最近更新 更多