在erase()之后保持有效的vector :: iterator

Inf*_*tor 11 c++ iterator

编辑:我有很多答案告诉我,我应该将删除分成另一个循环.也许我没有说清楚,但我在上一段中说过,我想找到一个解决方法.即保持当前的代码结构,但使用一些鲜为人知的C++ fu来使其工作.

好吧,我知道调用erase()一个向量会使元素的迭代器和它之后的所有迭代器失效,并且erase()会将迭代器返回到下一个有效的迭代器,但是如果擦除发生在其他地方呢?

我有以下情况(简化):

警告:不要认为这是整个代码.下面显示的内容非常简单,以说明我的问题.下面显示的所有类和方法实际上要复杂得多.

class Child {
   Parent *parent;
}

class Parent {
   vector<Child*> child;
}

void Parent::erase(Child* a) {
   // find an iterator, it, that points to Child* a
   child.erase(it);
}

int Child::update() {
   if(x()) parent.erase(*this) // Sometimes it will; sometimes (most) it won't
   return y;
}

void Parent::update() {
   int i = 0;
   for(vector<A>::iterator it = child.begin(); it != child.end(); it++)
      i += (*it)->update();
}
Run Code Online (Sandbox Code Playgroud)

因此,很明显,(*it)->update()如果x()返回true ,它会在运行后崩溃,因为当它执行时,Child会告诉Parent将它从向量中移除,使迭代器无效.

有没有什么方法可以解决这个问题,而不是让Parent::erase()迭代器一直传回去Parent::update()?这将是有问题的,因为它不会被调用每次调用Child::update(),因此该函数需要一种方法来每隔一次将迭代器返回给自己,并且它当前还返回另一个值.我还希望避免使用其他类似方法将擦除过程与更新循环分开.

Bjö*_*lex 5

我建议您重组代码,以免将更新(通过删除某些元素)和聚合(通过将值相加)这两种不同的操作混合在一起。

您可以通过将的返回值更改为Child::update,例如std::pair<int, bool>,其中int的值为值和bool指示是否应删除此元素的方法来执行此操作。

如果您可以创建Child::update一个const方法(意味着它不会修改对象,而仅调用其他const方法),则可以编写一个可与​​一起使用的简单函子std::remove_if。像这样:

class update_delete {
public:
    update_delete() : sum(0) {}
    bool operator()(const Child & child) {
        std::pair<int, bool> result = child.update();
        sum += result.first;
        return result.second;
    }
private:
    int sum;
}
Run Code Online (Sandbox Code Playgroud)

如果你不能做updateconst,只是交换与从后面的一些元素的元素(你必须保持一个迭代器总是指向可用于交换的最后一个元素)。汇总完成后,只需使用丢弃向量的末尾(现在包含所有要删除的元素)vector::resize。这类似于使用std::remove_if,但是我不确定是否有可能/有效地将其与修饰序列中对象的谓词一起使用。


Mac*_*cke 5

您实际上不能同时迭代std :: vector和对其进行变异,除非该变异的迭代之间存在某种通信。

我已经看到其他非标准容器通过“智能”迭代器来简化此过程,这些迭代器知道何时删除了它们的值(并且可能会自动跳转到下一个项目)。不过要记账得多。