我正在尝试对vector进行一些操作。并且仅在某些情况下调用向量上的擦除。
这是我的代码
while(myQueue.size() != 1)
{
vector<pair<int,int>>::iterator itr = myQueue.begin();
while(itr != myQueue.end())
{
if(itr->first%2 != 0)
myQueue.erase(itr);
else
{
itr->second = itr->second/2;
itr++;
}
}
}
我在第二次迭代中遇到崩溃。我发现这个崩溃与消息矢量迭代器不兼容。
崩溃的原因是什么?
答案 0 :(得分:8)
如果调用了erase()
,则迭代器失效,然后在循环的下一次迭代中访问迭代器。 std::vector::erase()
在擦除的迭代器之后返回下一个迭代器:
itr = myQueue.erase(itr);
答案 1 :(得分:2)
给定一个迭代器范围[b, e)
,其中b
是开始的e
,并且i
超出了向量范围的结尾,对某个迭代器i
进行了擦除操作范围将使e
到erase
的所有迭代器无效。这就是为什么在致电erase
时需要非常小心的原因。 itr = myQueue.erase( itr );
成员确实返回一个新的迭代器,你可以将它用于后续操作,你应该使用它:
i
另一种方法是交换i
元素和最后一个元素,然后删除最后一个元素。这样做效率更高,因为需要更少的元素移动myQueue.swap( i, myQueue.back() );
myQueue.pop_back();
。
vector
另外,从它的外观来看,你为什么要使用queue
?如果您需要std::queue
,也可以使用{{1}}。
答案 2 :(得分:2)
这是未定义的行为。特别是,一旦擦除迭代器,它就会变得无效,你就不能再使用它了。展开循环的惯用方法是:
for ( auto it = v.begin(); it != v.end(); ) {
if ( it->first % 2 != 0 )
it = v.erase(it);
else {
it->second /= 2;
++it;
}
}
但话又说回来,不用滚动你自己的循环而是使用算法会更有效率和惯用法:
v.erase( std::remove_if( v.begin(),
v.end(),
[]( std::pair<int,int> const & p ) {
return p.first % 2 != 0;
}),
v.end() );
std::transform( v.begin(), v.end(), v.begin(),
[]( std::pair<int,int> const & p ) {
return std::make_pair(p.first, p.second/2);
} );
这种方法的优点是在擦除时元素的副本数量较少(范围内剩余的每个有效元素将被复制不超过一次),并且更难弄错(即滥用无效的迭代器...)缺点是没有remove_if_and_transform
因此这是一个双遍算法,如果有大量元素,效率可能会降低。
答案 3 :(得分:2)
修改循环时迭代通常很棘手。
因此,有一个特定的C ++习语可用于非关联序列:擦除 - 删除习语。
它结合了remove_if
算法的使用和erase
方法的范围重载:
myQueue.erase(
std::remove_if(myQueue.begin(), myQueue.end(), /* predicate */),
myQueue.end());
其中谓词表示为典型的仿函数对象或使用新的C ++ 11 lambda语法。
// Functor
struct OddKey {
bool operator()(std::pair<int, int> const& p) const {
return p.first % 2 != 0;
}
};
/* predicate */ = OddKey()
// Lambda
/* predicate */ = [](std::pair<int, int> const& p) { return p.first % 2 != 0; }
lambda表单更简洁,但可能更少自我记录(没有名称),仅在C ++ 11中可用。根据您的喜好和限制,选择最适合您的。
可以提升编写代码的方式:使用Boost.Range。
typedef std::vector< std::pair<int, int> > PairVector;
void pass(PairVector& pv) {
auto const filter = [](std::pair<int, int> const& p) {
return p.first % 2 != 0;
};
auto const transformer = [](std::pair<int, int> const& p) {
return std::make_pair(p.first, p.second / 2);
};
pv.erase(
boost::transform(pv | boost::adaptors::filtered( filter ),
std::back_inserter(pv),
transformer),
pv.end()
);
}
您可以在文档中找到transform
和filtered
adaptor以及其他许多内容。