我应该如何存储大量不断变化的对象(例如子弹之类),并按其索引删除它们?听说vector::erase不太高效。
我应该如何存储大量不断变化的对象(例如子弹之类),并按其索引删除它们?听说vector::erase不太高效。
std::map
(或C++11的std::unordered_map
),这些容器在插入和删除操作的平均运行时间复杂度方面更好。 std::list
也是一个选择(它是显而易见的选择,但我先提到了其他容器,因为它们还允许快速查找/搜索,在许多游戏场景中甚至更加重要)。unordered_map
可能更合适。因为它的插入和删除时间复杂度为O(Log N),这会累积起来。 - Rookunordered_set<Bullet*>
就可以了。然后你可以简单地在任何地方使用 Bullet*
,并忘记索引。最好从对象池或类似的地方分配。
或者,由于标准友好地搞砸了接口,你可能需要使用 std::unordered_map<Bullet*, std::unique_ptr<Bullet>>
来获得更好的异常安全性等。
从我极其有限的游戏引擎编程二手知识来看,尽可能避免动态内存分配。因此,“大量”频繁删除的情况下,unordered_set
或list
都是不可取的。
编辑:在阅读了@James Kanze的答案后,我意识到@Cameron的建议将已删除的子弹与最后一个元素交换不起作用,因为它会改变活着的子弹的索引。我想你得使用James的建议,或者可能使用位向量来标记死亡的子弹。
将子弹保持紧凑排列在数组中也非常适合缓存性能。哈希表比std::map
的红黑树更好地缓存,但如果您已经按索引引用子弹,为什么要承担哈希的开销呢?
如果您通过向量中的索引来识别子弹,则erase
不是一个好主意;它会改变所有后续元素的索引。事实上,子弹是通过其索引进行标识,这在某种程度上限制了您的选择。在这种情况下,最好的解决方案可能是将条目标记为无效,并稍后重用它,可以通过在std::vector
中保留无效索引列表(很容易完成),或者仅通过每次想要创建新子弹时扫描向量来完成。如果您有不同类型的子弹(随着游戏的发展,您可能会有),则无论如何都需要动态分配子弹,将指针保存在向量中;空指针对于无效是一个不错的选择。
vector::erase
不是很高效,因为它需要移动所有后续元素以填充空间以保持顺序。但是,听起来您不需要保留顺序,在这种情况下,您可以通过用末尾的元素替换它,然后删除最后一个元素来更快地删除元素:
std::swap(bullets[index_to_remove], bullets.back());
bullets.pop_back();
vector
与list
和unordered_set
进行比较。 - Rook