在前面我们实现了vector和list
不管是库里面的使用
还是我们自己模拟实现的版本
今天来看一个关于容器容易出现的问题,迭代器失效问题
如果我们有如下要求,在一个list中,我们要求只将值为偶数的节点删除掉。
我们就实现如下代码:
void test_iterator_invalid() { list<int> L; L.push_back(1); L.push_back(2); L.push_back(3); L.push_back(4); L.push_back(5); //利用模板数打印list中的内容 PrintContainers(L); //要求只是删除值为偶数的节点 list<int> ::iterator it1=L.begin(); while(it1!=L.end()) { if(*it1%2==0)//为偶数 { L.erase(it1); } it1++; } cout<<endl; printf("删除值为偶数的节点\n"); PrintContainers(L); } 上面的代码 我们看上去没有什么问题,但是运行之后会崩掉
此处不上传崩溃的截图
分析原因: 
| list的迭代器失效是因为在进行erase()之后,节点被释放,但是当前迭代器还是指向该节点,node 节点已经被释放,node->next和node->prev都指向一块随机地址,我们也成为野指针,是不允许访问的。但是在循环中进行++操作时,其实是执行了node=node->next,访问非法地址,程序会崩掉。 |
//要求只是删除值为偶数的节点 list<int> ::iterator it2=L.begin(); while(it2!=L.end()) { if(*it2%2==0) {//保存当前要删除的位置 list<int> ::iterator to_delete=it2; it2++;//先向后走 L.erase(to_delete);//再删除该节点 } else { it2++; } } 上面这种方法是我们之前删除链表节点的方法,根据上一篇我们模拟库里面实现的 MyList 我们实现的Erase()的返回值也是迭代器,原理上来说,实现一个list 的erase(),我们是不需要返回值的 ,其实这里的返回值就是作用于这个地方,解决迭代器失效的问题的。
模拟实现list
利用erase()函数的返回值为iterator,返回的是删除节点的下一个位置的迭代器,此时我们就不用将迭代器继续向后走
list<int>::iterator it3=L.begin(); while(it3!=L.end()) { if(*it3%2==0) { it3=L.erase(it3); //用it3接收erase()的返回值,就不用向后++ } else { it3++; } } 总结list中迭代器失效:
链表删除节点导致的迭代器失效,容易造成访问野指针的问题,导致程序崩溃
同样上面的问题
我们需要删除顺序表中值为偶数的元素
我们可以很快的写下如下代码:
void test_vector_iterator_invalid() { vector<int> v1; v1.push_back(2); v1.push_back(5); v1.push_back(3); v1.push_back(1); v1.push_back(4); //用模板函数来实现打印顺序表 PrintContainers(v1); //实现值删除顺序表中值为偶数的元素 vector<int> ::iterator it1=v1.begin(); while(it1!=v1.end()) { if(*it1%2==0) { v1.erase(it1); } it1++; } PrintContainers(v1); } 猜猜看程序会不会崩掉,,,,,
算了还是先来用图分析一波 
我们看到 it++之后机会 直接越过了元素6,会导致少判断一个元素,造成逻辑错误,这样的结果并不是我们想要的,但是这里并没有访存出错的问题,我们猜想程序应该不会崩掉吧
结果是程序依旧崩掉了。
因为,但我们在进行erase()时,编译器内部是知道会存在这样的问题,所以它内部机制了如果用到erase()就必须接受返回值,否则就触发错误,也是为了防止迭代器失效的问题发生。
vector中实现erase()返回值也是迭代器,参考模拟实现vector
vector<int> ::iterator it2=v1.begin(); while(it2!=v1.end()) { if(*it2%2==0) { it2=v1.erase(it2);//用it2接收erase()的返回值,it2不进行向后走 } else { it2++; } } PrintContainers(v1); 那么这里是否可以向解决list 迭代器失效的方案一一样,先将此处迭代器保存下来,再进行++,然后erase(),我们说是不行的 
我们分析,当将 it1 先进行++后,it1就指向了下一个位置的,当进行erase()之后,it1 仍旧是在它的位置上,它里面的值也并不是我们期待的
为什么vector的插入会导致迭代器的失效?我们来看如下场景
void test_vector_iterator_invalid() { vector<int> v1; v1.reserve(5); v1.push_back(1); v1.push_back(2); v1.push_back(6); v1.push_back(4); v1.push_back(5); //用模板函数来实现打印顺序表 PrintContainers(v1); cout<<"Size:"<<v1.size()<<endl; cout<<"Capacity:"<<v1.capacity()<<endl; vector<int>:: iterator it3=--(v1.end()); v1.insert(it3,8);//在最后一个元素之前插入8 it3++;//对it3++ cout<<*it3<<endl; PrintContainers(v1); } 这里的演示结果在VS下观察得到。linux下的编译器会优化处理掉这个错误

这里进行图片解释程序崩溃的原因: 
完