C ++中用于不同派生类的高效容器

时间:2017-07-07 21:02:41

标签: c++ memory vector containers allocation

在编程游戏时,我曾经将所有游戏对象存储在具有初始化和固定大小的std :: vector中。最近我感觉需要在游戏对象类中进行一些继承。

所以我假设我有40个来自我的班级Ene​​my的课程。如果我想在向量中存储这些类的对象/实例,我只能选择将它们存储为向量Enemy *对吗?所以连续分配的唯一东西就是指针,对吗?所以当需要解除引用时,我仍然会有很多缓存未命中,对吧?

是否有任何"最佳做法"将派生类存储在连续分配的内存中的方式,以便循环它们花费最少的时间?

2 个答案:

答案 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的幂)替换...,以使Derived1Derived2的实例都适合向量。如果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"; }
};