最近偶然遇到了一个问题,某个人和我聊的时候问我,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不会对被删除的迭代器进行自减。吓我一跳,这纯属是瞎吉尔说。