浅谈迭代器失效【C++】

P. S.:以下代码均在VS2022环境下测试,不代表所有编译器均可通过。
P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。

  

在这里插入图片描述

                                           博主主页:LiUEEEEE
                                                C++专栏
                                              C语言专栏
                                            数据结构专栏
                                            排序算法专栏
                                         力扣牛客经典题目专栏

1、了解迭代器是什么


  1. 迭代器不是指针,是类模板,表现的像指针。他只是模拟了指针的一些功能,通过重载了指针的一些操作符,->,*,++ --等封装了指针,是一个“可遍历STL( Standard Template Library)容器内全部或部分元素”的对象, 本质是封装了原生指针,他可以根据不同类型的数据结构来实现不同的++,–,*等操作;

  2. 迭代器只能指向容器,而指针还可以指向函数,

  3. 迭代器返回的是对象引用而不是对象的值,所以cout只能输出迭代器使用*取值后的值而不能直接输出其自身。

  下面是通过对迭代器类型名称的打印所呈现的迭代器类型,由此可见,迭代器并非传统意义上的指针,而是与指针有类似功能的工具。
在这里插入图片描述




2、迭代器失效是指什么


  迭代器失效是指迭代器所指向的空间被销毁了,而我们重新使用这个指向被销毁空间的迭代器时会发生非法访问,所以编译器会对非法使用的迭代器进行报错处理。




3、迭代器为何会失效


  通过C++中 vector 和 string 类的学习可知,当我们对实例化的 vector 或 string 类对象进行数据插入时,如果发生了空间扩容,就会导致迭代器失效,其本质是,在扩容时,编译器会重新开辟一段已经扩容后的空间,并将原空间数据拷贝至新空间,而后将旧空间销毁,而此时迭代器所指向的依旧为旧空间的位置,所以会发生迭代器失效,此扩容过程类似于C语言中的realloc函数。
  迭代器失效过程如下示意图(此示意图是以通过迭代器对原vector空间第5个位置进行数据插入,当数据个数大于4时会发生扩容)

在这里插入图片描述
  而扩容结束后,再通过原本的迭代器进行操作时,就会发生非法访问,因为它指向的并不是新空间。


  C++中迭代器失效并不只发生在空间扩容的过程中,在不同编译器下,如果编译器在删除数据时,发生了缩容,那么也有可能发生迭代器失效,因为此情况的发生依据编译器的不同,故为了方便起见,C++委员会默认在删除数据时迭代器也会发生失效,所以当使用者删除数据后再使用迭代器访问,即使没有发生失效状况,也会报错。




4、在模拟实现vector或string时如何防止迭代器失效


  本篇以模拟实现vector为例,最好的防止措施就是提前备份好迭代器与起始位置的偏差值,在发生扩容后将迭代器更新,具体操作如下
//扩容
void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* tmp = new T[n];
		if(_start)
		{
			for (size_t i = 0; i < old_size; i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
	}
}

  本篇只展示了在扩容过程中的防治措施,如读者需要,可在自己实现的函数中自行更新。




5、模拟实现vector


  以下是vector的模拟实现代码,如有需要可自取进行测试。
#include <assert.h>
namespace LY
{
	template<class T>
	class vector
	{
	public:
		//可读可写迭代器
		typedef T* iterator;
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		
		//只读迭代器
		typedef const T* const_iterator;
		const_iterator begin()const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}
		
		//强制生成默认构造
		vector() = default;

		//拷贝构造函数
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (auto e : v)
			{
				push_back(e);
			}
		}

		//构造函数 初始化n个val
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		//构造函数 初始化数据
		vector(initializer_list<T> il)
		{
			reserve(il.size());
			for (auto e : il)
			{
				push_back(e);
			}
		}

		//迭代器区间初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//数据个数
		size_t size()const
		{
			return _finish - _start;
		}

		//空间个数
		size_t capacity()const
		{
			return _end_of_storage - _start;
		}

		//析构函数
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

		//扩容
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t old_size = size();
				T* tmp = new T[n];
				if(_start)
				{
					for (size_t i = 0; i < old_size; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + old_size;
				_end_of_storage = _start + n;
			}
		}

		//尾插数据
		void push_back(const T& x)
		{
			//if (_finish == _end_of_storage)
			//{
			//	size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
			//	reserve(newcapacity);
			//}
			//*_finish++ = x;
			insert(end(), x);
		}

		//可读可写运算符重载[]
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		//只读运算符重载[]
		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}

		//尾删数据
		void pop_back()
		{
			assert(_finish != _start);

			_finish--;
		}

		//指定位置插入数据
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
				pos = _start + len;
			}

			iterator end = _finish;
			while (end != pos)
			{
				*end = *(end - 1);
				--end;
			}
			*pos = x;
			++_finish;

			return pos;
		}

		//交换内容
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		//指定位置删除数据
		void erase(iterator pos)
		{
			assert(pos <= _finish);
			assert(pos >= _start);

			iterator end = pos;
			while (end != _finish - 1)
			{
				*end = *(end + 1);
				++end;
			}
			_finish--;
		}

		//运算符重载 =
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}




6、结语


在这里插入图片描述

  十分感谢您观看我的原创文章。
  本文主要用于个人学习和知识分享,学习路漫漫,如有错误,感谢指正。
  如需引用,注明地址。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值