当我使用vector :: erase时崩溃的原因是什么?

时间:2012-05-24 12:47:50

标签: c++ stl vector

我正在尝试对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++;
        }
    }
}

我在第二次迭代中遇到崩溃。我发现这个崩溃与消息矢量迭代器不兼容。

崩溃的原因是什么?

4 个答案:

答案 0 :(得分:8)

如果调用了erase(),则迭代器失效,然后在循环的下一次迭代中访问迭代器。 std::vector::erase()在擦除的迭代器之后返回下一个迭代器:

itr = myQueue.erase(itr);

答案 1 :(得分:2)

给定一个迭代器范围[b, e),其中b是开始的e,并且i超出了向量范围的结尾,对某个迭代器i进行了擦除操作范围将使eerase的所有迭代器无效。这就是为什么在致电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()
    );
}

您可以在文档中找到transformfiltered adaptor以及其他许多内容。