时间:2022-11-23 09:44:13 | 栏目:C代码 | 点击:次
我今天在使用迭代器时发现了一个问题,这个问题就是我在使用的迭代器时发现莫名其妙的有越界访问和获取的位置跟预期不符,经过一天的排查我发现不是所有情况下会出现这种问题,而是在容器删除和扩容时会发生越界或结果和预期不符的情况。
Text1
该函数的功能是把数组里面的所有偶数删除,遍历方式使用的是迭代器。
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v{ 1, 2, 3, 4, 5, 6 }; auto it = v.begin(); while (it != v.end()) { if (!(*it % 2)) { v.erase(it); } ++it; } return 0; }
当我运行起来时发现有越界访问和结果不符合预期这两种情况。
Test2
该函数的功能是想改变指定位置为30
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main(void) { vector<int> v{ 1, 2, 3, 4, 5 }; auto pos =find(v.begin(), v.end(), 3); v.reserve(100); *pos = 30; return 0; }
当该程序运行起来我发现出现了越界问题!
上面的问题我思考了许久都没想明白于是我开始去网找答案我找了一天看了数篇文章,终于解开困扰我多天的问题出现上述情况是因为迭代器失效了!!! 。
迭代器失效并不是说迭代器就是完全失效而是会出现以下两种情况:
1.迭代器的意义变了
2.迭代器完全失效
情况1是指迭代器没完全失效只是它表示的意义和原来的意思不同了,如果不做处理的话会导致运行结果会预期有偏差。如出现Test1那样的结果本来是想把所有偶数删掉结果迭代器失效了导致部分没删掉且还有越界访问风险。
那么,这种问题有什么较好的解决方案呢?其实很简单我们每次进行操作的时候都要更新下迭代器坐标即可
Test1修改后
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v{ 1, 2, 3, 4, 5, 6 }; auto it = v.begin(); while (it != v.end()) { if (!(*it % 2)) { //由于删除导致迭代器失效,所以我们重新更新下迭代器即可 it = v.erase(it); } else { //当迭代器不是偶数是才移动,如果所有情况都迭代的话会导致迭代器失效 ++it; } } return 0; }
情况2是指迭代器完全不能用了,如果还坚持使用会发生越界访问,因为此时的迭代器已经是一个野指针了迭代器的底层都是一个指针来维护的,当迭代器完全失效意味着该迭代器成为了野指针。
而Test2 是想修改指定位置的值,但是在修改前发生了一次扩容而该扩容就是导致迭代器失效的罪魁祸首,因为发生扩容的话原来的空间会被丢弃重新开辟一段内存来使用,而迭代器的底层是一个指针,它还指向之前的内存而该内存因为被释放了所以我们没有了使用权此时的迭代器也就成为了野指针。
那么,这种问题如何解决呢?其实和上一个问题的解决方案一样,当迭代器可能会发生变化时我们需要对迭代器进行一个更新确保它是有效的迭代器。
Test2修改后
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main(void) { vector<int> v{ 1, 2, 3, 4, 5 }; auto pos =find(v.begin(), v.end(), 3); //扩容导致迭代器成为野指针 v.reserve(100); //这时如果还想更改指定位置的值,那么我们需要进行一个迭代器的更新 pos =find(v.begin(), v.end(), 3); *pos = 30; return 0; }
当使用迭代器时只要内存或迭代器会发生变化,那么我们需要对迭代器进行一次更新确保它每次操作都一定是有效的,从而避免迭代器失效造成的越界访问和预期不符的情况。