c++ Vector之erase(导致迭代器失效)vector.erase源码解析

本文详细探讨了C++中删除vector元素时迭代器的行为,解释了在删除过程中迭代器如何处理以及vector内部的工作原理,通过源码分析澄清了关于迭代器是否自减的误解,并展示了程序运行结果,证明了正确使用vector.erase()方法可以有效删除所有匹配元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近偶然遇到了一个问题,某个人和我聊的时候问我,c++的vector如果进行某个元素删除的话,有哪些方法,我给他列举了一个方法,

#include <iostream>
#include <vector>
#include <stdio.h>
template <typename T>
void showvec(T& vec_)
{
    for(auto &p:vec_)
    {
        std::cout<<p<<"\t";
    }
    std::cout<<std::endl;
    return ;
}
int main() {
    int a[]={1,2,3,3,3,4,5,6,7,8};
    std::vector<int> ints_;
    for(int i=0;i<10;i++)
    {
        ints_.emplace_back(a[i]);
    }
    showvec(ints_);
    std::vector<int>::iterator it=ints_.begin();
    for(;it!=ints_.end();++it)
    {
        if(*it==3)
        {
            ints_.erase(it);
        }
        std::cout<<*it<<"\t";
    }
    return 0;
}

 

后来我一想,不对,这样的话应该会导致有一个元素3没有被删除,比如当it指向第一个3的时候,这时候的删除操作会导致第一个3后面的元素整体前移一位,这时候的数组是

{1,2,3,3,4,5,6,7,8},it指向现在的第一个3,然后再自增一位,所以这时候it会指向现在这个数组的第二个3,也就是原数组中的第三个3,所以原数组中间的那个3,是删除不掉的。但是那个人和我说,你再试一下,c++的迭代器其实会将它的指向默认-1,也就是说这么写不会有问题,这就导致我很疑惑,所以我索性查了一下vector.erase()的源代码,

首先是vector.erase的函数原型:

#if __cplusplus >= 201103L
      erase(const_iterator __position)
      { return _M_erase(begin() + (__position - cbegin())); }
#else
      erase(iterator __position)
      { return _M_erase(__position); }
#endif

可以看到erase会返回一个你所删除的位置的迭代器。

 

http://llvm-cs.pcc.me.uk/usr/include/c++/7.4.0/bits/vector.tcc中体现了_M_erase函数源代码如下:

 

template<typename _Tp, typename _Alloc>
    typename vector<_Tp, _Alloc>::iterator
    vector<_Tp, _Alloc>::
    _M_erase(iterator __position)
    {
      if (__position + 1 != end())
	_GLIBCXX_MOVE3(__position + 1, end(), __position);
      --this->_M_impl._M_finish;
      _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
      return __position;
    }

简单来讲_M_erase函数的作用就是,将__position之后的所有元素前移一位,但是那个_M_finish不清楚到底是一个计数?还是个末尾指针,于是看一下源码:

struct _Vector_impl
      : public _Tp_alloc_type
      {
	pointer _M_start;
	pointer _M_finish;
	pointer _M_end_of_storage;
        //........//
}

所以_M_finish是一个末尾的指针,这是将末尾指针前移,至于_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);这个函数,看一下函数的源码

      /**
       *  @brief  Destroy an object of type @a _Up
       *  @param  __a  An allocator.
       *  @param  __p  Pointer to the object to destroy
       *
       *  Calls @c __a.destroy(__p).
      */
      template<typename _Up>
	static void
	destroy(allocator_type& __a, _Up* __p)
	{ __a.destroy(__p); }

简单来说,因为end指针是指向末尾元素的下一个值,所以我们在删除一个元素的时候,它的操作是首先将所有的被删除元素的之后的值前移一位,然后将end指针自减1,这时候这个end指向的就是原来的vector中最后一个元素(注意这个时候是有两个最后的值,因为这个值在一开始被前移了1),然后删除end指向的这个元素,所以这时候的end还是指向了数组的最后一个元素的下一个值。

 

总体来看,erase的操作就是这些,所以我也贴一下程序的结果,

1    2    3    3    3    4    5    6    7    8    
1    2    3    4    5    6    7    8    

确实是erase不会对被删除的迭代器进行自减。吓我一跳,这纯属是瞎吉尔说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值