我有一个类是资源管理器,并将数据保存在<QString
,void*
>的映射中,该类如下所示:
template <typename R>
class ResourceManager
{
public:
ResourceManager() = default;
template <typename T>
void set(const R& name, T& object);
template <typename T>
T get(const R& name);
private:
QHash<R, void*> m_objectsMap;
};
template <typename R>
template <typename T>
void ResourceManager<R>::set(const R& name, T& object) {
m_objectsMap.insert(name, reinterpret_cast<void*>(&object));
}
template <typename R>
template <typename T>
T ResourceManager<R>::get(const R& name) {
auto it = m_objectsMap.find(name);
if (it == m_objectsMap.end()) throw std::invalid_argument("The item doesn't exists");
return *static_cast<T*>(it.value());
}
我有这个结构:
struct UserData {
QString username = "";
QString permissions = "";
QString token = "";
qint64 lastTimeUsed = 0;
UserData() {}
};
然后在以下功能中进行设置:
void f() {
UserData userData;
userData.username = userStruct.username;
userData.permissions = userStruct.permissions;
userData.token = token;
userData.updateLastTimeUsed();
qDebug() << "[Users][actionCheckToken]userData='" << userData.toString() << "'";
client.getResourceManager()->set<UserData>(USER_RESOURCEMANAGER_USERDATA_KEY, userData);
}
如果我在设置好之后立即调用get
,它会起作用,但是如果以后再调用,在另一个函数中,我会收到SIGSEGV
:
1 std::__atomic_base<int>::load atomic_base.h 396 0x55555556500e
2 QAtomicOps<int>::load<int> qatomic_cxx11.h 227 0x55555556500e
3 QBasicAtomicInteger<int>::load qbasicatomic.h 103 0x555555563e5e
4 QtPrivate::RefCount::ref qrefcount.h 55 0x5555555624a6
5 QString::QString qstring.h 958 0x5555555629a9
6 Users::UserData::UserData <- my struct Users.hpp 26 0x555555578cf1
7 ResourceManager<QString>::get<Users::UserData> ResourceManager.hpp 36 0x555555578df4
8 [function from where I call]
我检查了我的指针/引用是否有效,并且它们也指向正确的位置(同一资源管理器),但是我不知道它为什么崩溃,但是如果我在调用后立即调用set
有效。
以下是我稍后调用的函数的样子:
void b(Client& client) {
qDebug() << "[Users][userIsLogged]Called" << "clientID='" + client.getID() + "'";
auto userData = client.getResourceManager()->get<UserData>(USER_RESOURCEMANAGER_USERDATA_KEY);
// ...
}
答案 0 :(得分:0)
主要问题是您的ResourceManager
过于笼统。看一下它的名称:它管理资源。这意味着它需要能够拥有资源,这意味着获取,发布和 破坏 应该管理的资源。
您的资源管理器仅存储和检索对某些外部资源的引用(此处为void *指针)。在这种形式下,最好将其命名为ResourceDictionary
,这意味着所有权不存在,并且用户/调用者负责处理对象的生存期。
现在,要将您拥有的东西转换为真正的资源管理器,缺少一些关键数据:资源的类型。不知道在void *
中存储了什么 ,您将无法销毁(安全删除)。
但是您希望对资源管理器进行模板化,因此我建议采用这种方法:
struct tAbstractContainer
。当心它需要一个虚拟的析构函数!struct tQStringContainer : tAbstractContainer { QString string; };
set()
函数,并实现析构函数和赋值+复制(三/五规则)。现在您可以(几乎)将任何数据放入您的管理器中,它将被正确销毁。
为您的UserData
创建一个struct tUserDataContainer : tAbstractContainer { UserData userdata; };
。在f()
中,您将创建该容器的一个实例,并相应地填充其数据,然后将其扔给您的经理。