我在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();
有任何改进建议吗?
答案 0 :(得分:3)
1)个人偏好,但我会颠倒模板参数的顺序,并默认Key to 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<Key,boost::shared_ptr<T> >
这样的东西会破坏静态实例被破坏时的所有对象。 (虽然如果你关心破坏的顺序,那么你需要更聪明的东西 - 你可以从现有的单身解决方案中适应一些东西,如凤凰单身等)
4)您可以将迭代器更改为const_iterators。
5)破坏应该清除地图,以防止在调用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 :(得分:1)
一个改进是重写getRef
以使用getPtr
(反之亦然,方向与not repeating yourself无关):
static T& getRef(const Key& key)
{
return *getPtr(key);
}
答案 2 :(得分:0)
看来您做得非常出色。
顺便说一句,请问您为什么在这里投射实例(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_;