C++ 在一个映射中存储不同的指针类型(并处理销毁)
C++ storing different pointer types in one map (and handle the destroying)
在我的服务器项目中,我有一个连接 class,它处理一个到一个客户端的连接。在这方面 class 我想为未在连接 class 中定义的不同系统存储不同的数据 - 因为外部应该完全控制存储的数据。因此,例如,如果我想添加一个迷你游戏,我可以只将迷你游戏的数据(如 TMinigameData)添加到连接中,并在不更改连接中的任何内容的情况下只为这个迷你游戏使用它。
我目前的做法如下:
public:
template <typename T>
void clear_data()
{
auto it = _bind_data.find(typeid(T).hash_code());
if (it != _bind_data.end())
{
#ifdef _DEBUG
delete it->second.second;
#else
delete it->second;
#endif
_bind_data.erase(it);
}
}
template <typename T>
void bind_data(T*&& data)
{
bind_data(std::unique_ptr<T>(data));
}
template <typename T>
void bind_data(std::unique_ptr<T>&& data)
{
clear_data<T>();
#ifdef _DEBUG
_bind_data[typeid(T).hash_code()] = std::make_pair(sizeof(T), data.release());
#else
_bind_data[typeid(T).hash_code()] = data.release();
#endif
}
template <typename T>
T* get_data(bool create_if_not_exists = false)
{
auto it = _bind_data.find(typeid(T).hash_code());
if (it == _bind_data.end())
{
if (create_if_not_exists)
{
auto data_ptr = new T();
bind_data(std::unique_ptr<T>(data_ptr));
return data_ptr;
}
return nullptr;
}
#ifdef _DEBUG
assert(sizeof(T) == it->second.first, "Trying to get wrong data type from connection");
return (T*) it->second.second;
#else
return (T*) it->second;
#endif
}
private:
#ifdef _DEBUG
std::unordered_map<size_t, std::pair<size_t, void*>> _bind_data;
#else
std::unordered_map<size_t, void*> _bind_data;
#endif
这里的问题是不同数据的析构函数不会被调用,因为它是一个空指针。将它添加到我的地图时我知道类型,但之后它丢失了。我不知道如何存储特定对象的类型/析构函数....我的方法通常是错误的还是我应该怎么做?
有效解决方案的关键是虚拟继承或自定义删除器,因为 Rinat Veliakhmedov pointed out in his answer 已经如此。
但是,您不能直接使用虚拟 classes,因为您会遇到对象切片或无法在同一映射中使用任意类型的问题。
所以你还需要一层间接寻址。为了能够使多态方法起作用,您依赖于进一步的指针来避免对象切片,例如
std::unordered_map<size_t, std::pair<void*, std::unique_ptr<BaseDeleter>>>
std::unordered_map<size_t, std::unique_ptr<void*, std::unique_ptr<BaseDeleter>>>
在第一种情况下,您现在需要在地图外实现所有正确的删除,第二种情况根本不起作用,因为 std::unique_ptr
不能用作自定义删除器。在这两种情况下,您能做的最好的事情就是将整个内容包装在一个单独的 class 中,例如。 G。多态方法:
class DataKeeper
{
struct Wrapper
{
virtual ~Wrapper() { }
};
template <typename T>
struct SpecificWrapper : Wrapper
{
SpecificWrapper(T* t) : pointer(t) { }
std::unique_ptr<T> pointer;
};
std::unique_ptr<Wrapper> data;
public:
DataKeeper()
{ }
template <typename T>
DataKeeper(T* t)
: data(new SpecificWrapper<T>(t))
{ }
template <typename T>
DataKeeper(std::unique_ptr<T>&& t)
: DataKeeper(t.release())
{ }
};
现在我们有一个易于使用的 class DataKeeper
可以隐藏所有多态性的东西。我个人认为自定义删除器方法更简洁;为此,我们将从普通函数也可以用作自定义删除器这一事实中获益:
class DataKeeper
{
template <typename T>
static void doDelete(void* t)
{
delete static_cast<T*>(t);
}
std::unique_ptr<void, void(*)(void*)> pointer;
// ^ function pointer type
public:
DataKeeper()
: pointer(nullptr, nullptr)
{}
template <typename T>
DataKeeper(T* t)
: pointer(t, &DataKeeper::doDelete<T>)
// ^ instantiate appropriate template function and pass
// as custom deleter to smart pointer constructor
{ }
template <typename T>
DataKeeper(std::unique_ptr<T>&& t)
: DataKeeper(t.release())
{ }
};
您现在可以试试 e。 G。如下:
std::unordered_map<size_t, DataKeeper> map;
map[1] = DataKeeper(new int(7));
map.insert(std::pair<size_t, DataKeeper>(2, std::make_unique<double>(10.12)));
map.emplace(3, std::make_unique<std::string>("aconcagua"));
在析构函数中创建一个带有一些输出的 class,您会看到它被正确调用。
在我的服务器项目中,我有一个连接 class,它处理一个到一个客户端的连接。在这方面 class 我想为未在连接 class 中定义的不同系统存储不同的数据 - 因为外部应该完全控制存储的数据。因此,例如,如果我想添加一个迷你游戏,我可以只将迷你游戏的数据(如 TMinigameData)添加到连接中,并在不更改连接中的任何内容的情况下只为这个迷你游戏使用它。
我目前的做法如下:
public:
template <typename T>
void clear_data()
{
auto it = _bind_data.find(typeid(T).hash_code());
if (it != _bind_data.end())
{
#ifdef _DEBUG
delete it->second.second;
#else
delete it->second;
#endif
_bind_data.erase(it);
}
}
template <typename T>
void bind_data(T*&& data)
{
bind_data(std::unique_ptr<T>(data));
}
template <typename T>
void bind_data(std::unique_ptr<T>&& data)
{
clear_data<T>();
#ifdef _DEBUG
_bind_data[typeid(T).hash_code()] = std::make_pair(sizeof(T), data.release());
#else
_bind_data[typeid(T).hash_code()] = data.release();
#endif
}
template <typename T>
T* get_data(bool create_if_not_exists = false)
{
auto it = _bind_data.find(typeid(T).hash_code());
if (it == _bind_data.end())
{
if (create_if_not_exists)
{
auto data_ptr = new T();
bind_data(std::unique_ptr<T>(data_ptr));
return data_ptr;
}
return nullptr;
}
#ifdef _DEBUG
assert(sizeof(T) == it->second.first, "Trying to get wrong data type from connection");
return (T*) it->second.second;
#else
return (T*) it->second;
#endif
}
private:
#ifdef _DEBUG
std::unordered_map<size_t, std::pair<size_t, void*>> _bind_data;
#else
std::unordered_map<size_t, void*> _bind_data;
#endif
这里的问题是不同数据的析构函数不会被调用,因为它是一个空指针。将它添加到我的地图时我知道类型,但之后它丢失了。我不知道如何存储特定对象的类型/析构函数....我的方法通常是错误的还是我应该怎么做?
有效解决方案的关键是虚拟继承或自定义删除器,因为 Rinat Veliakhmedov pointed out in his answer 已经如此。
但是,您不能直接使用虚拟 classes,因为您会遇到对象切片或无法在同一映射中使用任意类型的问题。
所以你还需要一层间接寻址。为了能够使多态方法起作用,您依赖于进一步的指针来避免对象切片,例如
std::unordered_map<size_t, std::pair<void*, std::unique_ptr<BaseDeleter>>>
std::unordered_map<size_t, std::unique_ptr<void*, std::unique_ptr<BaseDeleter>>>
在第一种情况下,您现在需要在地图外实现所有正确的删除,第二种情况根本不起作用,因为 std::unique_ptr
不能用作自定义删除器。在这两种情况下,您能做的最好的事情就是将整个内容包装在一个单独的 class 中,例如。 G。多态方法:
class DataKeeper
{
struct Wrapper
{
virtual ~Wrapper() { }
};
template <typename T>
struct SpecificWrapper : Wrapper
{
SpecificWrapper(T* t) : pointer(t) { }
std::unique_ptr<T> pointer;
};
std::unique_ptr<Wrapper> data;
public:
DataKeeper()
{ }
template <typename T>
DataKeeper(T* t)
: data(new SpecificWrapper<T>(t))
{ }
template <typename T>
DataKeeper(std::unique_ptr<T>&& t)
: DataKeeper(t.release())
{ }
};
现在我们有一个易于使用的 class DataKeeper
可以隐藏所有多态性的东西。我个人认为自定义删除器方法更简洁;为此,我们将从普通函数也可以用作自定义删除器这一事实中获益:
class DataKeeper
{
template <typename T>
static void doDelete(void* t)
{
delete static_cast<T*>(t);
}
std::unique_ptr<void, void(*)(void*)> pointer;
// ^ function pointer type
public:
DataKeeper()
: pointer(nullptr, nullptr)
{}
template <typename T>
DataKeeper(T* t)
: pointer(t, &DataKeeper::doDelete<T>)
// ^ instantiate appropriate template function and pass
// as custom deleter to smart pointer constructor
{ }
template <typename T>
DataKeeper(std::unique_ptr<T>&& t)
: DataKeeper(t.release())
{ }
};
您现在可以试试 e。 G。如下:
std::unordered_map<size_t, DataKeeper> map;
map[1] = DataKeeper(new int(7));
map.insert(std::pair<size_t, DataKeeper>(2, std::make_unique<double>(10.12)));
map.emplace(3, std::make_unique<std::string>("aconcagua"));
在析构函数中创建一个带有一些输出的 class,您会看到它被正确调用。