c++ iterator 失效_c++之迭代器失效

1.首先从一到题目开始谈说起迭代器失效。有时我们很自然并且自信地 用下面方法删除vector元素:

#include

#include

#include

#include

#include

void del_elem(vector &vec, const char *elem)

{

vector::iterator itor =vec.begin();for (; itor != vec.end(); itor++)

{if (*itor ==elem)

{

vec.erase(itor);

}

}

}

template

voidshow_vec(InputIterator first, InputIterator last)

{while(first !=last)

{

std::cout<< *first << " ";

first++;

}

std::cout<< " " <<:endl>

}intmain(void)

{string arr[] = {"php", "c#", "java", "js", "lua"};

vector vec(arr, arr+(sizeof(arr)/sizeof(arr[0])));

std::cout<< "before del:" <<:endl>

show_vec(vec.begin(), vec.end());

del_elem(vec,"php");

std::cout<< "after del:" <<:endl>

show_vec(vec.begin(), vec.end());return 0;

}

当 string arr[] = {"php", "c#", "java", "js", "lua"}; 时,运行上边程序,得到如下输出:

运行结果是正确的啊。 找到 "php" ,然后删除,剩下四个元素。

但是实际上 del_elem 的过程是和我们想象的不一样的,在 del_elem中打印下每一步的 itor 的值,就会发现蛛丝马迹。

将 del_elem加上log:

void del_elem(vector &vec, const char *elem)

{

std::cout<< "----------------------------" <<:endl>

vector::iterator itor =vec.begin();for (; itor != vec.end(); itor++)

{

std::cout<< *itor <<:endl>

{

vec.erase(itor);

}

}

std::cout<< "----------------------------" <<:endl>

}

我们在做删除操作前,打印每个元素的值, 继续编译运行得到如下结果:

在做 del_elem操作时,少打印了一个 "c#", 也就是在打印完"php",然后删除php以后,接下来打印的不是 "c#", 而直接打印了 "java" 。

那么我们可以将 vec.erase(itor) 注释掉,然后 可以得到 del_elem 会打印所有的元素值,

如此看来 c# 是因为执行了erase 操作以后,“变没了”。

弄清这个问题,我们要看看一组vector操作的定义:

iterator erase(iterator position)

{if(position + 1 !=end())

copy(position+ 1, finish, position);--finish;

destroy(finish);returnposition;

}

iterator begin() { return start; }

iterator end() { return finish; }

我们经常使用  vec.begin(), vec.end(), 想必也能知道start和finish 为何物。

首先看erase函数: 先判断 待删除的迭代器是否为 finish 的前一个元素,也就是vector中的最后一个元素,

如果不是最后一个元素,就将待删除迭代器的后边所有元素往前移动一个slot, 然后 --finish  重新定位finish指针。

此时finish指针指向的是没删除之前的最后一个元素地址,本例中就是 lua的地址, 但是当前的finish指针处的值已经没用了,于是调用destroy。

如果待删除迭代器是finish的前一个元素的话,那么就直接移动finish指针,调用destroy销毁该位置处的元素对象。

与此同时,我们看到erase函数传进来的迭代器,只起到了一个位置定位判断的作用,erase本身没有对该迭代器有任何操作,该迭代器的值所在地址仍然有效,但是由于进行了copy操作,position处的值已经变成了"c#".

再回过头来看一下我们的 del_elem 函数:

当删除第一个元素php之后,在执行 itor++之前,php之后的所有元素都前移了一个slot导致此时 itor存放的元素已经是 c#,

于是继续执行itor++后,此时itor又向后移动,itor中的值已经是java,c#就是这样被漏掉的。

1-2 由此,又可以得出另一个结论,当arr中有n(n>=2)个 连续的php元素,我们用 del_elem函数 是不能删除掉所有的php元素的,于是这样就会导致bug。

我们将 string arr[] = {"php", "c#", "java", "js", "lua"}; 改为 string arr[] = {"php", "php", "php", "php", "c#", "java", "js", "lua"}; 后,观察运行结果:

固然不出所料,php没有被删除干净,因为当删除第一个php以后,用当前 del_elem 方法,总是会漏掉删除的php之后的元素,如果这个元素恰好是 "php",便会出现bug。

1-3 用当前 del_elem删除一个元素,会导致 finish 前移一个slot,如果将php放到最后slot,即finish之前的slot中,当删除最后一个php后,finish会指向删除的php的地址(已经非法了),

然后php的地方会被销毁,然后又执行 itor++,于是此时的itor指向到finish的后边,当 判断 itor != vec.end() 时,这个式子是成立的,于是继续执行,但是当对迭代器解引用时,最终会由于是非法

引用地址,程序崩掉。我们来看一下是否是这样, 将最后一个元素改为 "php"; string arr[] = {"php", "php", "php", "php", "c#", "java", "js", "php"};

编译运行,结果如下:

gdb调试,发现是因为 *itor 导致程序崩溃。

2 以上例子指出了vector 删除元素时选择的方法不当导致的一些问题;

1 是删除多个相同元素时,因为vector自身的特性导致 删除不净,出现bug

2 是当删除的元素时最后一个元素,可能导致程序崩溃。

3 我们很多时候都知道vector 迭代器失效会出问题,但是很多时候不知道会导致什么问题。

以上例子列举了 迭代器失效的 结果, 那么反过来 ,我们再研究 “什么是vector删除元素会导致迭代器失效” 的问题。

我的结论是,在对vector进行删除元素的时候, 删除元素之前,假设我们定义了一些迭代器分别指向,

1 迭代器的位置位于 待删除迭代器之前,

2 待删除的迭代器

3 迭代器的位置位于 待删除的迭代器之后

那么当对待删除的迭代器调用erase(itor)以后,之前定义在itor之前的迭代器依旧有用, 之前定义的 itor 以及 itor之后的迭代器 已经失效了,这里的失效是指,这些迭代器所指的元素内容已经和删除之前的不一样了,甚至可能是指向了非法地址。

于是在对这些失效的迭代器进行操作的时候 可能导致程序出bug ,或者直接崩溃。

4. 那么该如何删除vector元素呢?

可以参考:

void del_elem(vector &vec, const char *elem)

{

vector::iterator itor =vec.begin();for (; itor !=vec.end();)

{

std::cout<< *itor << " " << &(*itor) <<:endl>

{

itor=vec.erase(itor);                 //个人觉得这句赋值是多余的,因为erase本身没有对itor进行任何操作,erase操作之前和操作之后的itor所指向的位置是不变的,变的只是里边的值。 如有理解错误,还望及时指出

}else{

itor++;

}

}

}

5. 想必读者已经对vector删除元素引起的迭代器失效有了一些理解,那么再来理解插入元素导致的迭代器失效会更容易一些。

1 如果插入操作引起了空间重新配置,(申请新空间,赋旧空间的值到新空间,释放旧空间),那么在插入操作执行后,之前声明的所有迭代器都将失效

2 如果没有引起空间配置,那么会导致插入位置之后的迭代器失效。

6. 我们 假如我们声明了一些迭代器,对vector进行了插入或删除操作以后,要注意这些迭代器可能已经失效。

7. 由vector的迭代器失效,可以引出,其他序列式容器的迭代器失效,其他关联式容器的迭代器失效。内容太多,本篇只是先给出vector的迭代器失效的一些理解,后续继续补充其它的。

第九条,较详细的讨论的各种容器的操作方法,有兴趣的读者可自行翻阅。

水平有限,错误难免,望及时指出。希望能对大家理解迭代器失效提供一些思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值