vector 迭代器失效问题

 

目录

1、vector实现的底层成员变量 

2、插入数据insert成员函数的模拟实现

2.1插入函数insert成员函数迭代器失效的问题

2.2如何解决insert成员函数迭代器失效的问题

 3、插入数据erase成员函数的模拟实现

3.1插入函数erase成员函数迭代器失效的问题

 3.2如何解决erase成员函数迭代器失效的问题


迭代器其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,即如果继续使用已经失效的迭代器,程序可能会崩溃。


1、vector实现的底层成员变量 

vector的迭代器失效问题,主要会发生在插入insert函数接口 以及 删除erase函数接口中。

那么首先我们需要知道vector的底层结构是如何实现的。

 从上图可知,容器vector实现的成员变量 有迭代器start、迭代器finish、迭代器end_of_storage。

迭代器start指向的是容器vector里存放的第一个有效数据位置

迭代器finish指向的是容器vector里存放的最后一个有效数据位置的下一个位置

迭代器end_of_storage指向的是容器vector里容量capacity的下一个位置


2、插入数据insert成员函数的模拟实现

2.1插入函数insert成员函数迭代器失效的问题

插入数据insert成员函数会发生迭代器失效的问题,所以我们通过其模拟实现来看看如何导致的迭代器失效的问题。

    template<class T>
	class vector
	{
	public:
        //插入数据的函数
		void insert(iterator pos, const T& x)
		{
			//断言:位置pos(迭代器)必须正常
			assert(pos >= _start);
			assert(pos <= _finish);

			//空间不够就要扩容
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}

			// 挪动数据——要挪的数据,从后往前挪
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;

			//插入一个数据后,迭代器_finish要加加一次
			//迭代器_finish指向最后一个有效数据的空间 的下一个
			++_finish;
		}


	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
    };

 这里会出现迭代器失效的是迭代器pos,即外部传进函数的迭代器pos,如果我们在使用插入函数insert后,在函数外部继续使用迭代器pos进行操作,可能就会出现问题。

主要原因是因为我们在函数内部会进行空间不够的扩容处理,即使用成员函数reverse,因为扩容会另外开辟新空间,所以原来的迭代器pos指向的空间就被销毁了,所以在函数内部会改变pos的值,而插入函数的是用的传值传参,所以改变函数内部的迭代器pos并不会影响外部的迭代器,所以如果发生了扩容处理,那么这个时候外部的迭代器就不能再使用,否则就会出现迭代器失效的 问题,导致系统奔溃。


2.2如何解决insert成员函数迭代器失效的问题

我们上面的模拟实现,与stl中标准库里vector的insert成员函数实现是一样的。

那么我们在模拟实现的基础上,如何解决这个问题了??

方法一:外部的迭代器pos最好 不要再使用。
方法二:返回改变的迭代器pos,及时更新外部迭代器pos。

但是方法二库里的这个函数 不是这样设计的,库里这个函数并没有返回,和上面的代码一样。

对于方法二的模拟实现:

 template<class T>
	class vector
	{
	public:
        //插入数据的函数
		iterator insert(iterator pos, const T& x)
		{
			//断言:位置pos(迭代器)必须正常
			assert(pos >= _start);
			assert(pos <= _finish);

			//空间不够就要扩容
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}

			// 挪动数据——要挪的数据,从后往前挪
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;

			//插入一个数据后,迭代器_finish要加加一次
			//迭代器_finish指向最后一个有效数据的空间 的下一个
			++_finish;

            return pos;
		}


	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
    };

函数内部改变的迭代器pos作为函数值返回,然后在函数外部接收返回值,这样就能达到更新函数外部迭代器pos的值,那么这个时候,在函数外部继续使用该迭代器就不会出现迭代器失效的问题,也就不会导致系统崩溃。

再次说明,stl标准库vector里的insert函数,并没有设置返回值!


 3、插入数据erase成员函数的模拟实现

3.1插入函数erase成员函数迭代器失效的问题

 插入数据erase成员函数会发生迭代器失效的问题,所以我们通过其模拟实现来看看如何导致的迭代器失效的问题。

 

 template<class T>
	class vector
	{
	public:
        //删除数据的函数
		void erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			//将pos位置后的数据往前移动,是从前面的数据开始往前移动
			while (begin != _finish) {
				*(begin - 1) = *begin;
				++begin;
			}

			//删除一个数据后,迭代器_finish要--
			--_finish;

		}


	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
    };

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效。

但是,如果pos刚好是指向最后一个元素删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,该位置迭代器就失效了。


 3.2如何解决erase成员函数迭代器失效的问题

方法:返回改变的迭代器pos,及时更新外部迭代器pos。
库里的这个函数 就是 这样设计的,这个和insert相反。

 template<class T>
	class vector
	{
	public:
        //删除数据的函数
		iterator erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			//将pos位置后的数据往前移动,是从前面的数据开始往前移动
			while (begin != _finish) {
				*(begin - 1) = *begin;
				++begin;
			}

			//删除一个数据后,迭代器_finish要--
			--_finish;

            return pos;
		}


	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
    };

函数内部改变的迭代器pos作为函数值返回,然后在函数外部接收返回值,这样就能达到更新函数外部迭代器pos的值,那么这个时候,在函数外部继续使用该迭代器就不会出现迭代器失效的问题,也就不会导致系统崩溃。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值