C++多实例模式的模板化类实现

发布于 2024-08-23 03:29:08 字数 1544 浏览 7 评论 0 原文

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

有什么改进建议吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尸血腥色 2024-08-30 03:29:08

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* 使用更智能的容器,则可以避免调用 Foo::destroy()。
类似于 std::map 的东西> 会在静态实例被销毁时销毁所有对象。 (尽管如果你关心破坏的顺序,那么你需要一些更聪明的东西 - 你可以从现有的单例解决方案中调整一些东西,例如phoenix单例等)

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

这就是我现在能想到的。

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.

得不到的就毁灭 2024-08-30 03:29:08

一项改进是重写 getRef 以使用 getPtr (反之亦然,方向并不重要,不重复自己):

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

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);
}
梦里泪两行 2024-08-30 03:29:08

看来你的工作做得非常令人印象深刻。

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

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_;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文