编辑:我有很多答案告诉我,我应该将删除分成另一个循环.也许我没有说清楚,但我在上一段中说过,我想找到一个解决方法.即保持当前的代码结构,但使用一些鲜为人知的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(),因此该函数需要一种方法来每隔一次将迭代器返回给自己,并且它当前还返回另一个值.我还希望避免使用其他类似方法将擦除过程与更新循环分开.
我建议您重组代码,以免将更新(通过删除某些元素)和聚合(通过将值相加)这两种不同的操作混合在一起。
您可以通过将的返回值更改为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)
如果你不能做update它const,只是交换与从后面的一些元素的元素(你必须保持一个迭代器总是指向可用于交换的最后一个元素)。汇总完成后,只需使用丢弃向量的末尾(现在包含所有要删除的元素)vector::resize。这类似于使用std::remove_if,但是我不确定是否有可能/有效地将其与修饰序列中对象的谓词一起使用。
您实际上不能同时迭代std :: vector和对其进行变异,除非该变异的迭代之间存在某种通信。
我已经看到其他非标准容器通过“智能”迭代器来简化此过程,这些迭代器知道何时删除了它们的值(并且可能会自动跳转到下一个项目)。不过要记账得多。