赞
踩
项目中的观察者模式中,用了QList来保存观察者对象,在回调函数的过程中,会遍历这些观察者,去进行通知调用。
这里回调存在2个问题:
先给出结论:不能在range-for的循环体中改变遍历的容器的大小,即不允许遍历的同时添加或删除元素!
至于原因,其实也不难理解:
我们都知道,凡是使用了迭代器的循环体中都不能向迭代器所属的容器添加元素!(C++primer,5e,P99)
这是因为range-for底层实现时预存了容器的end()值,而一旦遍历的时候向该容器添加或删除元素,
就会使该预存的end()失效,由上述迭代器失效的问题,
就不难明白:range-for的循环体中不允许对该容器添加或删除元素!
而且这种错误一旦发生,很难发现错误根源,编译期无错误无警告,
而且运行时不同编译器执行的结果可能不一样!因为迭代器失效后再执行后续循环将是未定义的行为。
解决办法:
所以C++primer建议如果使用迭代器遍历,每次在插入或删除元素后都应该重新定位迭代器。要么就采用能每次自动更新迭代索引和序号的循环方法,或者自己主动更新迭代器。
解决办法示范1:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
更具生成性的模板方法,erase返回下一个有效迭代器的位置:
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
逐个擦除元素很容易导致N^2性能。最好标记应该擦除的元素,并在循环后立即擦除它们。如果我可以假定nullptr在向量中不是有效元素,那么
std::vector<IInventory*> inv;
// ... push some elements to inv
for (IInventory*& index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
{
delete index;
index =nullptr;
}
}
inv.erase( std::remove( begin( inv ), end( inv ), nullptr ), end( inv ) );
您不能在循环迭代期间删除迭代器,因为迭代器计数不匹配,并且在某些迭代之后,您可能会得到无效的迭代器。
解决方案: 1)获取原始向量的副本 2)使用此副本迭代迭代器 3)做一些事情并将其从原始向量中删除。
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> copyinv = inv;
iteratorCout = 0;
for (IInventory* index : copyinv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
inv.erase(inv.begin() + iteratorCout);
iteratorCout++;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。