在编程游戏时,我曾经将所有游戏对象存储在具有初始化和固定大小的std :: vector中。最近我感觉需要在游戏对象类中进行一些继承。
所以我假设我有40个来自我的班级Enemy的课程。如果我想在向量中存储这些类的对象/实例,我只能选择将它们存储为向量Enemy *对吗?所以连续分配的唯一东西就是指针,对吗?所以当需要解除引用时,我仍然会有很多缓存未命中,对吧?
是否有任何"最佳做法"将派生类存储在连续分配的内存中的方式,以便循环它们花费最少的时间?
答案 0 :(得分:2)
Boost刚刚接受了一个库,正是出于这个目的:poly_collection
。特别是,您正在寻找base_collection
在内部,它使用多个向量,每个(派生)类型一个,同时提供接近标准容器的接口。
其作者的 This article提供了一些设计背景和与其他解决方案的比较,例如unique_ptr
的向量。优点是双重的:首先,通过不使用指针和每个元素的动态内存分配,你有更好的内存局部性,第二,将相同类型的元素组合在一起,you help branch prediction和虚拟成员函数的指令缓存。
答案 1 :(得分:0)
那呢?
struct alignas(...) Base {};
struct Derived1 : Base {};
struct Derived2 : Base {};
int main()
{
std::vector<Base> v(2);
new (&v[0]) Derived1();
new (&v[1]) Derived2();
return 0;
}
新的展示位置可以解决问题。它适用于多态性。 alignas是为了确保向量包含相同大小的对象。用数字(2的幂)替换...
,以使Derived1
和Derived2
的实例都适合向量。如果sizeof(Derived1)
返回16,而sizeof(Derived2)
返回24,则需要一个alignas(32)
。
编辑
如@KubaOber所述,alignas
并非旨在管理分配大小,而仅是为了将对象放置在内存中。实现目标的更好方法是使用std::variant
。像这样:
int main()
{
std::vector<std::variant<Derived1, Derived2>> v;
v.emplace_back(Derived1());
v.emplace_back(Derived2());
for (const auto& e : v)
std::visit(VisitPackage(), e);
return 0;
}
其中VisitPackage
可能是这样的:
struct VisitPackage
{
void operator()(const Derived1&) { std::cout << "Derived 1.\n"; }
void operator()(const Derived2&) { std::cout << "Derived 2.\n"; }
};