使用字节向量作为其他类型的原始存储是一种好的做法吗

Is it a good practice to use a vector of bytes as raw storage for other types?

本文关键字:一种 存储 向量 字节 原始 类型 其他      更新时间:2023-10-16

我开始在YouTube上学习ECS教程,以前从未见过有人将新变量分配到uint8vector中。

template<typename Component>
uint32 ECSComponentCreate(Array<uint8>& memory, EntityHandle entity, BaseECSComponent* comp)
{
uint32 index = memory.size();
memory.resize(index+Component::SIZE);
Component* component = new(&memory[index])Component(*(Component*)comp);
component->entity = entity;
return index;
}

(有问题的完整代码可以在这里找到;这里的Array#define Array std::vector(

它与使用指针向量有什么不同,为什么它更好?

这基本上是一个"池分配器"。既然你知道了它的名字,你就可以了解它为什么这么做了,但性能通常是动机。

所有的分配都是在一个向量中完成的,最后整个向量可以立即解除分配(在销毁其中的对象之后,您可以在下面的解除分配函数中看到(。

使用vector字节对我来说有两个问题:

  1. 对齐。尽管您可以通过一个对齐的分配器来解决这个问题。如果您在运行时之前还不知道要提前存储哪些类型及其对齐要求,那么稍微浪费一点(但对于少数大型容器来说就不那么浪费了(就是对动态分配使用最大对齐
  2. 在我的书中,这是一个更大的难题,这与在向vector插入元素时vector如何重新分配其数组有关。如果你在其中存储非平凡的构造性/可破坏性类型,那么在没有正确调用必要的移动/复制器和dtor的情况下,在内存中复制它们的字节可能会造成严重破坏

如果你想让你的ECS在内存中连续存储全新的组件类型,那么我建议一种危险性小得多的方法是抽象你的组件容器,比如:

struct Components
{
virtual ~Components() {}
};
template <class T>
struct ComponentsT: public Components
{
std::vector<T> components;
};

当你想向系统注册一个新的组件类型Foo时,你可以动态分配和实例化ComponentsT<Foo>,将其粘贴在多态容器(容器的容器(中,当你想将Foo组件添加到实体中时(因此实例化它并将其添加到组件列表中(,获取相关的抽象Components*基指针/ref,将其下变频(可以使用dynamic_cast(到ComponentsT<Foo>*,然后将您的组件实例推回到那里。现在,您可以将所有的Foos连续存储在内存中,而无需处理对齐和无效等问题。

相关文章: