我刚刚对一个奇怪的设计进行了技术测试,在第一次感受到厌恶之后,我开始看到一些有用的用例。我想知道你对此有何看法。 练习目标:
实现一个模板类SingletonWrapper,它可以用任何数据类型(T)实例化,它应该包含一个数据类型T的变量**wrapped**以及相同的getter setter。任何特定数据类型一次只能存在一个实例。尝试创建已存在的相同类型会引发错误。 ...剩下的练习...
所以这是我刚刚为这篇文章所做的尝试(文件Singleton_template.h):
#include <vector>
#include <unordered_map>
#include <memory>
#include <typeindex>
#include <exception>
template<typename T>
class Singleton_template
{
public:
Singleton_template(T ob)
{
std::cout << "Singleton_template()" << std::endl;
std::type_index typeIdx = typeid(T);
if (m_singletons.find(typeIdx) == m_singletons.end())
{
this->m_Wrapper = ob;
m_singletons[typeIdx] = this;
}
else
{
std::cout << "Already exist" << std::endl;
throw std::exception("Already exist");
}
}
virtual ~Singleton_template()
{
if (auto it = m_singletons.find(typeid(T)); it != m_singletons.end())
{
m_singletons.erase(it);
std::cout << "~Singleton_template() deletion" << std::endl;
}
}
static T Get(T ob)
{
return Get();
}
static T Get()
{
if (auto it = m_singletons.find(typeid(T)); it != m_singletons.end())
{
return (*it).second->m_Wrapper;
}
return NULL;
}
static void Set(T ob)
{
if (auto it = m_singletons.find(typeid(T)); it != m_singletons.end())
{
(*it).m_Wrapper = ob;
}
}
void operator=(T ob)
{
Set(ob);
}
T operator==(T ob)
{
return(Get(ob));
}
T operator<<(T ob)
{
return(Get(ob));
}
friend std::ostream& operator<<(std::ostream& os, const Singleton_template<T>& singleton) {
os << Get();
return os;
}
private:
static std::unordered_map<std::type_index, Singleton_template<T>*> m_singletons;
T m_Wrapper;
};
template <typename T>
std::unordered_map<std::type_index, Singleton_template<T>*> Singleton_template<T>::m_singletons;
以下是如何使用它:
Singleton_template<int> mint = 118;
Singleton_template<std::string> mstring = std::string("Hello");
std::cout << "" << Singleton_template<int>::Get() << " - " << mstring << std::endl;
Singleton_template<int> hello = 2;
我认为这种设计可以相关的一个场景是一个单例持有者工厂/构建器,其中每个构建对象都以预设对象(所有对象的“ObjectComponent”之母)开始,构建器从它的成员值开始创建所需的对象目的。因此我们可以更新任何工厂(将更新集合构建器)或特定构建器。
我不确定这是否是一个正确的设计,您对这种结构的实用性有什么建议或用例吗?
我不太确定你的设计,甚至不确定你对问题练习的理解......从你所描述的情况来看,所需要的正是这样的:
#include <stdexcept>
#include <iostream>
template <typename T>
class SingletonWrapper
{
public:
SingletonWrapper()
{
// since singletons are usially instatiated at module level,
// this will bomb before the program starts.
if (initd)
{
std:: cout << "singleton for this type is already created\n";
exit(3);
}
initd = true;
}
static void set(T t)
{
wrapped = std::move(t);
}
static T& get()
{
return wrapped.get();
}
private:
static bool initd;
static T wrapped; // this would be the usual go-to choice.
};
template <typename T>
inline T SingletonWrapper<T>::wrapped;
template <typename T>
inline bool SingletonWrapper<T>::initd = false;
class A {};
SingletonWrapper<A> sa;
SingletonWrapper<A> sb; // we'll try to create and initialize a second singleton of type A
int main()
{
sa.set(A{});
}
这不是我设计单例的方式,因为这种设计对包装类提出了一些相当严格和不寻常的要求(其中之一必须至少是可移动的)。这就是为什么单例通常存储指向对象的指针,这意味着您不需要这个时髦的计数器。
我的意思是,您应该探索使用指针,并且可能仅在调用 set() 时抛出异常,这是通常完成的方式。