我最近发现了面向数据的设计的好处。看起来很令人印象深刻其中一点是按类型和访问对数据进行分组,而不是在对象中,而是在数组中,以防止缓存未命中和更好的处理。
所以在游戏中我们仍然有实例,用户可以销毁它们中的任何一个(不仅仅是阵列中的最后一个)。我无法弄清楚如何有效地处理数组中间的对象删除。
我有一个想法:拥有isAlive
值,但这将对条件数量产生很大影响,因为每个对象在处理,绘图中都会被多次检查......
另一个想法是将整个数组移位以填充必须删除的空间,但这会在删除时消耗大量资源。
国防部怎样才能解决这个问题?
所以提出要求:
答案 0 :(得分:4)
实际上很简单,不要使用直接引用。使用一个间接层,如ID。例如:
假设您有一个管理所有Foo“对象”的FooManager(不是OOP意义上的对象,每个Foo属性的数组集合)。据我所知,你现在所做的只是返回一个索引。假设Foo#42是Foo,其数据位于所有阵列的索引42处。现在你要删除Foo#42,它会在你的数组中打个洞。您可以移动所有其他数组条目,但随后Foo#43不再指向实际索引43。
因此我们添加一个ID表,而不是返回索引,而是返回一个ID。当您创建新的Foo时,将其数据添加到数组中的第一个空闲索引(假设为42)。然后你生成一个新的未使用的ID(比方说1337)。现在你可以更新ID表(一个(哈希)映射很适合这个)说ID 1337指向索引42.现在你可以将ID 1337返回给调用函数。 (注意调用函数如何找不到实际存储数据的位置,这是无关的信息)
下次需要从另一段代码更新Foo时,会使用ID。因此,使用ID 1337调用FooManager的setBar函数,并将新的Bar值作为参数调用。 FooManager在其ID表中查找1337,发现它仍位于索引42处,并使用新的Bar值更新Bar数组索引42。
现在这是这个系统得到它的值的地方,让我们删除Foo 1337.使用ID 1337作为参数调用FooManager的removeFoo。它查找1337,它位于索引42.但是,与此同时,增加了10个新的Foos。所以我们现在可以做的只是将索引42处的值替换为52处的值,实际上将52移动到42.这将导致旧系统中的大问题,但现在,我们只需要更新索引表。因此,我们查找哪些ID指向52(假设它是1401)并将其更新为42.下次有人尝试更新ID为1401的Foo时,它会在索引表中查找并发现它位于索引42处。
因此我们将数据保存在连续内存中,删除只需要非常少量的数据副本操作(Foo的每个属性一个副本),而FooManager的“外部”代码甚至从未意识到发生了什么。它甚至解决了死亡的反驳问题。假设一些代码仍然具有删除的1337 ID(悬空refence,这很糟糕!),当它试图访问/更改它时,FooManager查找它,看到1337不存在(任何更多)并且可以生成一个很好的干净警告/ error和callstack,它允许您直接识别哪些代码仍然具有悬空引用!
只有一个缺点,即ID表中的额外查找。现在哈希表可以非常快,但每次修改Foo对象时它仍然是一个额外的操作。但是,在大多数情况下,从经理外部访问的次数远远少于在经理内部访问。当你有一个BulletManager时,它会每帧更新每个项目符号,但是访问项目符号以更改/请求某些内容并调用创建/删除项目符号的可能性较小。 如果这是另一种方法,您应该更新您的数据结构以针对该情况进行优化。再说一次,在这种情况下,数据位于内存中的情况并不重要,因此您可以使用数组中的“漏洞”,甚至使用完全不同的数据布局。
答案 1 :(得分:0)
它取决于约束和工作量,但一种方法是将已删除的元素与数组中的最后一个元素交换,然后从末尾删除已删除的元素(pop_back
)。这假设数组的顺序不是特别重要。另一种方法是稀疏数组,它可能在内存预算固定的环境中工作。
编辑:如果您正在维护数组中的外部指针,则可以使用智能指针管理它们,当移动数组元素时,智能指针的基础地址会更新(shared_ptr::reset
)。你最终得到2个相同长度的并行数组:
typedef std::vector<thing> thingVec;
thingVec things;
// smart pointers for each object
std::vector<boost::shared_ptr<thingVec::iterator>> references;
在createThing
函数中,您将返回一个带有自定义删除器的shared_ptr
(一旦所有引用都消失,它将自动执行数组更新):
http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/sp_techniques.html#static
智能指针可以指向具有多个字段的结构,这些字段最终存储在不同的数组中,只要自定义删除程序知道在删除元素时要压缩哪些数组。