代码
#pragma once
#include<string>
#include<unordered_map>
namespace MyStd
{
class RuntimeDataManager
{
private:
class RuntimeDataBase
{
friend class RuntimeDataManager;
protected:
virtual void ReleaseData() = 0;
std::string id;
public:
RuntimeDataBase() {}
RuntimeDataBase(const std::string& _id) :id(_id) {}
RuntimeDataBase(const RuntimeDataBase& d) :id(d.id) {}
std::string GetID()const
{
return id;
}
};
std::unordered_map<std::string, RuntimeDataBase*>data;
public:
template<typename T>
class RuntimeData :public RuntimeDataBase
{
friend class RuntimeDataManager;
private:
template<typename... Args>
RuntimeData(const std::string& _id, Args&&... args) :RuntimeDataBase(_id), value(new T(std::forward<Args>(args)...)) {}
template<typename... Args>
RuntimeData() : value(nullptr) {}
T* value;
protected:
virtual void ReleaseData()
{
delete value;
}
public:
RuntimeData(const RuntimeData& data) :RuntimeDataBase(data), value(data.value) {}
operator T*()const
{
return value;
}
T& operator* ()const
{
return *value;
}
T* operator-> ()const
{
return value;
}
};
template<typename T>
RuntimeData<T> GetData(const std::string& id)
{
if (auto iter = data.find(id); iter != data.end())
{
RuntimeData<T>* t = dynamic_cast<RuntimeData<T>*>(iter->second);
if (t == nullptr)
return RuntimeData<T>();
return *t;
}
return RuntimeData<T>();
}
bool FindData(const std::string& id)
{
return data.find(id) != data.end();
}
template<typename T, typename...Args>
RuntimeData<T> AddData(const std::string& id, Args&& ...args)
{
RemoveData(id);
data.emplace(id, new RuntimeData<T>(id, std::forward<Args>(args)...));
return GetData<T>(id);
}
bool RemoveData(const std::string& id)
{
if (auto iter = data.find(id); iter != data.end())
{
iter->second->ReleaseData();
delete iter->second;
data.erase(iter);
return true;
}
return false;
}
template<typename T>
bool RemoveData(const RuntimeData<T>& iter)
{
return data.erase(iter.id);
}
~RuntimeDataManager()
{
for (const auto& i : data)
{
i.second->ReleaseData();
delete i.second;
}
}
};
}
在RuntimeDataManager中,每一个数据都有唯一对应的id,类似于变量名,所有数据都存储在一个以id为key的map中,通过id可以对数据进行操作。RuntimeData类相当于该管理器的迭代器,是数据和其id的组合。设置RuntimeDataBase类则是为了使所有数据类型都有一个共同的基类,这样才能存在同一个容器中。
使用方法
注意:在同一个RuntimeDataManager中,id是唯一的,不能重复,同时也没有“访问权限”的概念,只要能访问manager对象,就能访问该对象中所有数据。但是,可以通过创建多个manager使数据更加有条理。
迭代器类型
迭代器即RuntimeDataManager::RuntimeData类。
对于迭代器类型,可以使用GetID()获取其id。此外,它重载了*,->运算符,可以当作指针使用,也可以隐式转换为指针。
添加数据
auto iter=manager.AddData<数据类型>(该数据的id,初始化列表);
- 添加数据时,如果该id已经存在,会自动删除原数据。
- “初始化列表”即该数据构造函数的参数,可以有多个。
- 返回值为指向添加的数据的迭代器。
查找数据
auto iter = manager.GetData<数据类型>(该数据的id);
//或
bool b = manager.FindData(该数据的id);
- 第一种方法返回指向该数据的迭代器。它但要求指定的id存在,还要求id对应的数据类型必须是用户指定的数据类型。如果没有找到该数据,或数据类型不匹配,迭代器对应的指针为nullptr。
- 第二种方法只用于查询指定的id是否存在,对数据类型没有要求。
删除数据
bool b = manager.RemoveData(该数据的id);
- 如果指定的id存在,删除并返回true;否则返回false。
实际应用
该类主要用于回调函数之间的数据传递。例如,开发一个服务端程序,当客户端请求“答题”时,服务端从题库中随机选一道题发送给客户端,接着客户端再传来答案,服务端要判断正误,就必须记录上一次选取的题目的答案。而一般来说选题与判断正误在不同的回调函数中,互相传递数据很不方便,并且在这个例子中数据需要动态创建、删除,所以用一个专门的类来管理非常必要。上面的例子用以下代码可以实现:
struct Question
{
std::string mQuestion, mAnalytic;
char mAnswer;
Question(const std::string& question, char answer, const std::string& analytic)
:mQuestion(question), mAnalytic(analytic), mAnswer(answer) {}
};
void onRequestQuestion(const std::string& clientid)
{
Question* q = data.GetData<Question>(clientid + "_question");
if (q == nullptr)
{
//从题库中选题
...
q = data.AddData<Question>(clientid + "_question",...);
}
SendMsg(clientid, q->mQuestion);
}
void onAnswerQuestion(const std::string& clientid,char answer)
{
if (auto q = data.GetData<Question>(clientid + "_question"))
{
if (answer == q->mAnswer)
{
SendMsg(clientid, "恭喜,回答正确!");
}
else
{
SendMsg(clientid, "很遗憾,回答错误!");
}
data.RemoveData(q);
}
else
SendMsg(clientid, "你还没有开始答题!");
}