【问题标题】:What STL container to perform removal of elements in between?什么 STL 容器执行删除其间的元素?
【发布时间】:2011-07-12 23:47:04
【问题描述】:

我需要选择一个容器来保存指向我定义的类型 (Particle) 的指针。我正在使用预分配的粒子Object Pool(其中包含在 std::vector 上预分配的对象)。

我的粒子发射器在需要发射时向粒子池询问粒子(以避免游戏中的粒子分配)。当粒子过期时,它会返回到粒子对象池中。

如您所见,当我遍历我的粒子参考容器(需要选择一个)以更新它时,我必须检查哪些粒子已过期 (lifetime <= 0.0) 并将它们返回到粒子池, 过期粒子可以在容器中的任何位置。

我一直在考虑使用std::list,原因如下:

列表 (AFAIK) 在开始时提供恒定时间插入,并在任何点提供恒定时间删除(假设您已经迭代到该点)。

欢迎对我的系统提出任何建议或改进,以便更好地适应您的容器建议。

编辑

为了更好地解释我自己:

发射器中粒子的寿命完全相同,它取决于一个范围,例如 5.0 秒 +-(0.0 到 0.5)。这是为了给粒子一个随机元素,在固定时间内看起来比所有的都好。

算法伪代码:

    // Assume typedef std::container_type<Particle *> ParticleContainer

void update(float delta)
{   
    ParticleContainer::iterator particle = m_particles.begin();   

    for(; particle != m_particles.end(); ++particle)
    {
        updateParticle(*particle, delta);         //Update the particle

        if ( (*particle)->lifeTime <= 0.0 )
        {
            ParticlePool.markAsFree(*particle);   //Mark Particle as free in the object Pool
            m_particles.remove(*particle);        //Remove the Particle from my own ParticleContainer
        }   
    }
}

【问题讨论】:

  • 对于顺序容器,始终以 std::vector 开头。然后配置文件,如果容器操作有问题,请尝试另一个容器。 Usually you will find yourself sticking with std::vector.
  • “常数时间”和std::list的问题在于常数很大!使用 std::vector 时间是可变的,但很小。你的选择! :-)

标签: c++ stl particles


【解决方案1】:

我不完全遵循您的算法,但需要std::vector 来提供摊销恒定时间push_back。迭代时它还可能具有更好的参考局部性。

如果顺序无关紧要,删除任何项目也是一个常数时间操作:

template <typename T>
void remove(std::vector<T> &v, size_t i)
{
    std::swap(v[i], v.back());
    v.pop_back();
}

【讨论】:

  • 我认为正确的术语是amortized constant time,但是,是的,从std::vector 开始,只有在使用另一个容器时分析显示改进后才会更改。 However, I doubt you will find this often.
  • 使用std::swap(v[i], v.back()); v.pop_back(); 会更好,因为swap 是不抛出的、恒定的时间和便宜的(对于swap 的所有非白痴实现)。
  • 你好,我编辑了我的帖子。在一个元素上调整矢量大小的成本是多少?当然这比删除中间的元素要好,但是,如果我经常这样做,你认为性能会受到太大影响吗? (不过我得介绍一下)
  • @Mr.Gando,添加单个元素需要摊销的常数时间。在典型的实现中,重新分配会使向量的容量翻倍,因此每次元素数量翻倍时才需要它。
  • 太棒了!我一直在寻找一种快速、高效(恒定时间)的方法来通过随机访问删除元素。谢谢!
【解决方案2】:

为什么不使用priority queue?这样您就不必遍历所有活动粒子。

编辑:再想一想:我不太确定这是否真的有效,这取决于您的算法(我承认我并不完全理解)。如果您要更改该生命周期价值当条目在容器中时,队列可能根本不起作用。

但是,如果您不更改该值(例如,您在插入粒子时设置粒子过期的时间,然后只检查第一个条目与当前时间的对比)我仍然认为这是您的最佳选择。 (请注意,优先级队列只是一个适配器,默认在内部使用std::vector。)

edit2:关于您的编辑。我建议不要为每个粒子存储一个“生命周期”值(它会不断减少,直到它不再是正数),而是存储一个时间戳,表示应该何时移除粒子。初始化粒子时,只需计算粒子何时到期(通过将您的随机“生命周期”添加到当前时间)。这个值不会在你的粒子的生命周期内改变。在判断是否移除粒子时,只需检查当前时间是否大于过期时间戳即可。

【讨论】:

  • 我编辑了我的帖子,生命周期值没有改变,但对于所有粒子来说并不相同。粒子寿命确实取决于随机“范围”值,例如:5.0 秒 +- 范围(0.0, 0.5)(其中范围给出给定参数之间的随机数)
  • 是的,但您似乎可以在插入时确定“到期日期”,只需将您的(随机)生命周期添加到当前时间,然后存储结果。 (在粒子从容器中取出之前,初始化后过期日期不会改变)。优先级队列将确保内部的所有粒子都是弱排序的,因此您只需弹出条目,直到未来的最高“到期日期”。
【解决方案3】:

假设您不需要直接索引 (operator[]) 并且您通常只是在容器上进行迭代,list 听起来不错。您甚至可以使用splice 在恒定时间内移动列表节点,而无需分配或释放内存。

【讨论】:

  • 您好,我编辑了我的帖子,如果您认为您的回复仍然适用,那就太好了:)。
【解决方案4】:

听起来像std::list 是要走的路。这是假设您肯定要在从中间删除对象的同时遍历列表。请注意,当您从中间移除时,大多数其他容器将使迭代器无效; std::list 没有。其他建议:

  • 如果你(很多)关心空间,你可以试试Boost.Intrusive
  • 如果您愿意使用非标准容器,请尝试slist(单链表;gcc 支持这一点)——它将节省空间,在删除元素时为您节省一些(少量)运行时成本,因为您'正在减少必须更新的指针数量。这与具有双重链接的std::list 进行比较。

【讨论】:

    【解决方案5】:

    我没有完全理解,但是;

    • 一组粒子指针也可以证明是值得的任务。插入日志(n) 删除日志(n)

    • 1234563删除

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-07
      • 1970-01-01
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      相关资源
      最近更新 更多