vector中迭代器失效的问题及解决办法
创始人
2024-05-30 19:25:50
0

 

目录

vector常用接口 

vector 迭代器失效问题 

vector中深浅拷贝问题 


vector的数据安排以及操作方式,与array非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变;要换个大(或小) 一点的房子,可以,一切琐细得由客户端自已来:首先配置一块新空间, 然后将元素从旧址一 一搬往新址,再把原来的空间释还给系统。vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。因此,vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始就要求一个大块头array了,我们可以安心使用vector,吃多少用多少。

vector定义

template
 class vector 

{

public:
        typedef T* iterator;
        typedef const T* const_iterator;

private:

        iterator _start ;  //表示目前使用空间的头
        iterator _finish;  //表示目前使用空间的尾
        iterator _end_of_storage;  //表示可用空间的尾

}

vector常用接口 

  • push_back( ) 成员函数在vector的末尾插入值,如果有必要会扩展vector的大小。
  • pop_back( ) 成员函数在vector的末尾删除值。
  • size( ) 函数显示vector的大小。
  • begin( ) 函数返回一个指向vector开头的迭代器。
  • end( ) 函数返回一个指向vector末尾的迭代器。
  • empty() 判断vector是否为空。
  • find() 查找。(注意这个是算法模块实现,不是vector的成员接口)
  • insert()  在position之前插入val
  • erase() 删除position位置的数据
  • swap() 交换两个vector的数据空间
  • operator[]  像数组一样使用下标访问

size 是当前 vector 容器真实占用的大小,也就是容器当前拥有多少个容器。

capacity 是指在发生 realloc 前能允许的最大元素数,即预分配的内存空间。

当然,这两个属性分别对应两个方法:resize() 和 reserve()

使用 resize() 容器内的对象内存空间是真正存在的。

使用 reserve() 仅仅只是修改了 capacity 的值,容器内的对象并没有真实的内存空间(空间是"野"的)。

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。 具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。 reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。 resize在开空间的同时还会进行初始化,影响size。

此时切记使用 [] 操作符访问容器内的对象,很可能出现数组越界的问题。

vector 迭代器失效问题 

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。迭代器失效就是迭代器底层对应指针所指向的空间倍销毁了,导致使用了一块已经被释放了的空间。

迭代器失效分为两大类:

1.扩容导致野指针

 

我们发现push_back尾插4个后调用insert会出现随机值。问题就是扩容导致pos迭代器失效,原因在于pos没有更新,导致非法访问野指针。

当尾插4个数字后,再头插一个数字,发生扩容,根据reserve扩容机制,扩容地址改变,迭代器就会失效,insert中发生扩容,迭代器指向的空间被释放,迭代器本质上就是一个野指针。_ start和_ finish都会更新,但是这个插入的位置pos没有更新,此时pos依旧执行旧空间,再者reserve后会释放旧空间,此时的pos就是野指针,导致*pos = x就是对非法访问野指针。因为pos迭代器没有更新,所以后续挪动数据并没有实现,而插入数据是对释放的空间进行操作,同样没有意义。这也就是说不论你在哪个位置插入,都没有效果。

解决办法:

扩容后更新pos,解决pos失效的问题。

iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);//扩容地址改变,迭代器会失效//insert中发生扩容,it指向的空间被释放,it本质上就是一个野指针if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//扩容后更新pos,解决pos失效的问题pos = _start + len;}iterator end = _finish - 1;while (pos >= end){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}

2.迭代器指向位置意义改变

比如要求删除vector中所有的偶数

 erase删除pos位置元素后,pos位置之后的元素会往前移动,没有导致底层空间的改变,理论上讲迭代器不会失效,但是如果pos位置刚好是最后一个元素,删完之后pos刚好是end的位置,而end的位置是没有有效元素的,那么pos就失效了。因此删除vector中任意位置元素时,均认为该位置上迭代器失效。我们应该在使用的时候注意,让迭代器指向有效的位置。

迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃。

vector中深浅拷贝问题 

拷贝构造函数

 memcpy是浅拷贝,当T是内置类型的时候这个拷贝函数没什么问题,当时当T是自定义类型的时候就会出现问题,比如T是string类型。

 

 如果此时我们使用的是memcpy函数进行拷贝构造的话,那么拷贝构造出来的vector中每个string的成员变量的值,将与被拷贝的vector中每个string的成员变量的值相同,即两个vector当中的每个对应的string成员都指向同一个字符串空间。

解决办法: 

_start[i] = _v[i] 本质是调用string类的赋值运算符重载函数进行深拷贝。 

扩容也需要注意浅拷贝的问题。

扩容时调用的memcpy是浅拷贝,就会导致先前存储的数据被memcpy后再delete就全删掉变成随机值了。vector调用析构函数析构掉原来的对象,每个对象又调用自身的析构函数,把指向的空间释放掉,然后就会出现随机值。

 我们析构旧空间的时候,析构的是对象数组,每个数组调用自身的析构函数,会析构数组的空间。我们用memcpy浅拷贝时,拷贝的临时对象和原来的对象指向同一块空间,所以旧空间被销毁后,我们扩容的新空间中的对象变成野指针,访问的数据都是随机值。我们用for循环调用vector的赋值运算符重载可以将旧空间的数据拷贝到新空间,这样析构旧空间就不会影响新空间。

相关内容

热门资讯

手机装虚拟安卓系统教程,手机安... 你有没有想过,你的手机可以变成一个多才多艺的小戏精,既能装Windows,又能玩安卓?没错,这就是今...
安卓系统平板电脑屏幕暗,打造舒... 你是不是也遇到了这样的烦恼?你的安卓系统平板电脑屏幕突然暗了下来,亮度调节按钮也似乎失灵了。别急,让...
安卓高清系统推荐电脑版,打造极... 你有没有想过,手机上的安卓高清系统竟然也能搬到电脑上?没错,就是那种流畅、美观,还能让你在电脑上也能...
自己刷安卓盒子系统,功能、操作... 你有没有想过,家里的安卓盒子系统是不是该来个焕然一新的大变身呢?别急,让我带你一步步走进自己刷安卓盒...
安卓系统的空格怎么输入,探索安... 你是不是也和我一样,在使用安卓手机的时候,有时候会遇到一个让人头疼的问题——怎么输入空格呢?别急,今...
安卓系统如何删除僵尸粉 你是不是也遇到了这样的烦恼?手机里安卓系统的社交软件里,突然冒出了好多僵尸粉,这些家伙不仅不互动,还...
安卓平板电脑能刷系统,解锁无限... 你有没有想过,你的安卓平板电脑是不是也能来个“变身大法”,换换新系统呢?没错,今天就要来聊聊这个话题...
安卓双系统机器人,探索安卓双系... 你知道吗?在科技飞速发展的今天,手机已经不仅仅是一个通讯工具了,它还能变成一个多才多艺的小助手。最近...
ubuntu安装安卓系统下载,... 哇,你有没有想过,在你的Ubuntu电脑上安装一个安卓系统呢?想象你可以在同一个设备上无缝切换使用W...
安卓怎么进入闪氪系统,操作指南... 你有没有想过,你的安卓手机里隐藏着一个神秘的闪氪系统?别惊讶,这可不是科幻小说里的情节,而是真实存在...
如何关掉安卓系统通知,享受宁静 手机通知这东西,有时候真是让人又爱又恨。你有没有遇到过这种情况:手机里弹出来的通知一个接一个,让你分...
安卓系统为啥不更新了,安卓系统... 你有没有发现,最近你的安卓手机好像有点儿“懒”了,更新系统的时候总是慢吞吞的,甚至有时候直接告诉你:...
安卓系统手电在哪里打开,安卓手... 你有没有在安卓手机上找过手电筒功能,结果却像在茫茫大海中捞针一样?别急,今天就来给你详细揭秘,安卓系...
安卓系统个版本通用吗 你有没有想过,你的安卓手机上的系统版本是不是和别人的手机一样通用呢?这个问题听起来可能有点奇怪,但确...
安卓玩鸿蒙系统好吗,安卓用户玩... 你有没有想过,把安卓手机换成鸿蒙系统,会是怎样的体验呢?最近,这个话题在互联网上可是掀起了一阵热潮。...
安卓开源报名系统源码,架构设计... 你有没有想过,那些在手机上报名参加各种活动、课程或者考试的安卓应用,其实背后有一个神秘的“大脑”——...
峰米系统是安卓系统吗,揭秘安卓... 你有没有听说过峰米系统?最近这个话题在数码圈里可是挺火的。很多人都在问,峰米系统是安卓系统吗?今天,...
华为系统emui和安卓系统的区... 你有没有发现,手机的世界里,系统就像是它们的灵魂,决定了它们能跳得多高、飞得多远。今天,咱们就来聊聊...
朗逸安卓系统车载导航,安卓系统... 你有没有想过,开车的时候,导航系统就像是个贴心的导航小精灵,带你穿梭在城市的每一个角落?今天,就让我...
排球鹰眼系统辅助器安卓,安卓排... 你有没有想过,在观看排球比赛时,那些精准的鹰眼系统是如何让比赛画面更加清晰、精彩呢?现在,就让我带你...