STL之迭代器失效
什么是迭代器失效
对于容器,对元素做插入和删除操作之后,原本可以通过一个迭代器来访问的容器中的元素变得不能再通过这个迭代器访问了,因为插入和删除操作可能更改了元素在内存中的位置,原来这个迭代器指向的内存已经不是原来的元素所在的内存了。其实从内存的角度考虑程序存在问题真是一个非常直观的并且容易理解的方法。
vector的迭代器失效
从下面这个程序开始,目的是删除容器中的可以被n整除的数字
typedef vector<int>vec;
typedef vector<int>::iterator it;
void print(vec&v) {
it iter;
for (iter = v.begin(); iter != v.end(); iter++) {
cout << *iter ;
}
cout << endl;
}
void del(vec&v, int n) {
it iter;
for (iter = v.begin(); iter != v.end(); iter++) {
//占据连续内存的容器,删除一个元素之后所有的元素会自动调整
//if (*iter%n == 0) {
// v.erase(iter);
//}
if (*iter%n == 0) {
//erase会返回下一个元素的迭代器,自动调整容器中元素的位置
iter=v.erase(iter);
}
else
{
iter++;
}
}
}
int main()
{
vector<int>ans;
for (int i = 0; i < 10; i++) {
ans.push_back(i);
}
cout << "删除元素之前的vector中的数" << endl;
print(ans);
cout << "删除被2整除的数:" << endl;
del(ans, 2);
print(ans);
return 0;
}
需要熟记的点:
占用连续内存的容器:
- 删除元素:如果删除的容器末尾的元素,那指向容器末尾的迭代器肯定失效,如果删除的是任意一个其他元素,那会使得指向这个元素的迭代器和后面所有元素的迭代器都会失效,因为元素在连续内存空间上发生了整体移动,直接进行iter++会使得迭代器指向一个未知的内存。
- 插入元素:如果插入元素,就要考虑插入一个元素之后容器的size是否会超过capacity,如果插入元素之后容器的size还小于容器的capacity,再看插入到容器末尾还是中间,如果插入元素之后容器的size大于capacity,那容器中的元素就会整体拷贝到一个新的1.5或者2倍的内存空间中,这样所有的迭代器都会失效。
- 后置++d的底层实现(VS):可以看出
_Vector_iterator operator++(int)
{ // postincrement
_Vector_iterator _Tmp = *this;
++*this;
return (_Tmp);
}
list的迭代器失效
链表型的比较好理解,增加任何元素都不会使得任何迭代器失效,记得插入元素一定要先将后面的保存下,防止找不到后面的链表头,删除元素也只是指向当前被删除元素的迭代器会失效,其他迭代器都不会失效,类似map吧。
map的迭代器失效
从下面这个程序开始
typedef map<int, int>mymap;
typedef map<int, int>::iterator it;
void print(mymap&m) {
it iter;
for (iter = m.begin(); iter != m.end(); iter++) {
cout << iter->first<<":"<<iter->second<<"." ;
}
cout << endl;
}
void del(mymap&m, int n) {
it iter;
for (iter = m.begin(); iter != m.end(); iter++) {
if (iter->second%n == 0) {
//还是重载的++代码,先执行++,此时迭代器已经++了,而返回的元素还是要删除的元素
m.erase(iter++);
//或者可以这样
//类似做题的时候删除一个节点的操作
//先保留要删除的节点迭代器,然后让他指向下一个有意义的节点,然后删除这个节点
//auto tmpiter = iter;
//iter++;
//m.erase(tmpiter);
}
else
{
iter++;
}
}
}
int main()
{
mymap m;
for (int i = 0; i < 10; i++) {
m[i] = i;
}
cout << "删除元素之前的vector中的数" << endl;
print(m);
cout << "删除被2整除的数:" << endl;
del(m, 2);
print(m);
return 0;
}
需要熟记的点:
- map等关联式容器,底层是红黑树,删除一个元素之后整棵树会自动调整的,但是单个节点在内存地址中没有变化,变化的是节点间的指向关系,
- 参考源码:1.先进行了iter++,2.iter指向了下一个元素3.返回的是当前要删除的元素
_Tree_iterator& operator++()
{ // preincrement
++static_cast<_Mybase&>(*this);
return (*this);
}
_Tree_iterator operator++(int)
{ // postincrement
_Tree_iterator _Tmp = *this;//记录当前的对象
++*this;//调用重载的++
return (_Tmp);//返回当前的对象
}