【问题标题】:Deleting elements from a vector从向量中删除元素
【发布时间】:2012-02-10 13:45:38
【问题描述】:

以下 C++ 代码用多个对象填充一个向量,然后删除其中一些对象,但看起来它删除了错误的对象:

vector<Photon>  photons;

photons = source->emitPhotons();    // fills vector with 300 Photon objects

for (int i=0; i<photons.size();  i++) {
    bool useless = false;

    // process photon, set useless to true for some

    // remove useless photons
    if (useless) {
        photons.erase(photons.begin()+i);
    }
}

我这样做正确吗?我在想photons.erase(photons.begin()+i); 这行可能是问题所在?

【问题讨论】:

    标签: c++ vector


    【解决方案1】:

    这绝对是错误的做法,你永远不会在删除时调整i..

    使用迭代器,这个问题就消失了!

    例如

    for(auto it = photons.begin(); it != photons.end();)
    {
      if (useless)
        it = photons.erase(it);
      else
        ++it;
    }
    

    还有其他使用算法的方法(如remove_iferase等),但上面最清楚...

    【讨论】:

    • warning: 'auto' will change meaning in C++0x; please remove it|, error: ISO C++ forbids declaration of 'it' with no type|
    • @Ben,我偷懒了,auto 是 c++11 中的一个关键字,非常方便,您需要将 auto 更改为一个类型,例如在这种情况下:vector&lt;Photon&gt;::iterator
    • +1 它让我想起了this pull request。人们经常犯这个错误。
    • Thx,虽然是简短的后续问题:如何使用这种方法添加额外的光子,即在 else 情况下?
    • @Ben,添加到向量将使迭代器无效,并且无法从中恢复(至少不是在循环期间 - 除非您从头开始重新启动),另一种方法可能是将它在一个新向量中,然后在循环完成后,将所有项目从新向量复制到photons 向量。
    【解决方案2】:

    优雅的方式是:

    std::vector<Photon> photons = source->emitPhotons();
    photons.erase(
          std::remove_if(photons.begin(), photons.end(), isUseless),
          photons.end());
    

    和:

    bool isUseless(const Photon& photon) { /* whatever */ }
    

    【讨论】:

    • 不错。您是否还建议使用列表而不是向量?如果是列表,是否允许我在“for循环”内的数据结构中插入一个元素?
    • @Ben 如果你使用erase-remove-idiom,就像我展示的那样,就不会有 for 循环。也无法修改photons 内的isUseless()
    • @Karl von Moor 再次,您是否建议在我的情况下将您的erase-remove-idom 更改为使用std::list 而不是std::vector?
    • @Ben 我推荐 std::vector,参见here
    【解决方案3】:

    正确的版本如下所示:

    for (vector<Photon>::iterator i=photons.begin(); i!=photons.end(); /*note, how the advance of i is made below*/) {
       bool useless = false;
    
       // process photon, set useless to true for some
    
       // remove useless photons
       if (useless) {
         i = photons.erase(i);
       } else {
         ++i;
       }
    }
    

    【讨论】:

      【解决方案4】:

      在这种情况下,您应该使用 stl::list。引用 STL 文档:

      列表具有重要的属性,即插入和拼接不会使列表元素的迭代器失效,即使移除也会使指向被移除元素的迭代器失效。

      所以这将遵循以下原则:

      std::list<Photon> photons;
      photons = source->emitPhotons();
      std::list<Photon>::iterator i;
      for(i=photons.begin();i!=photons.end();++i)
      {
          bool useless=false;
          if(useless)
              photons.erase(i);
      }
      

      【讨论】:

      • 这使我的程序崩溃。我已经修改它以将 i++ 从 for 循环中取出,并将其放在 else 情况下。并在删除前添加it =。这行得通,但实际上感觉它比我之前使用的矢量方法需要更长的时间!?
      【解决方案5】:

      擦除向量中间的元素是非常低效的……其余元素需要“移”回一个槽,以填充调用创建的向量中的“空”槽erase。如果您需要擦除列表类型数据结构中间的元素而不会产生这样的惩罚,并且您不需要 O(1) 随机访问时间(即,您只是试图将元素存储在您将在以后复制或在其他地方使用的列表,并且您始终遍历列表而不是随机访问它),您应该查看std::list,它使用底层链表来实现它,给它 O(1)修改列表的复杂性,如插入/删除。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-05
        • 2020-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多