【c++】——详解迭代器失效的底层原理

一、迭代器为什么会失效

在讲解这个问题之前,我们来模拟一些场景。

场景一:把vec容器中所有的偶数全部删除
代码实现如下:

vector<int> vec;
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
	auto it = vec.begin();
	for (; it != vec.end(); ++it)
	{
		if (*it % 2 == 0)
		{
		vec.erase(it);
		break;
		}
	}

如果有break,运行结果如下:
在这里插入图片描述
表示只删除一个迭代器,没有问题

如果没有break,运行结果如下:
在这里插入图片描述
发现进程意外中断,进程不是正常结束运行——迭代器失效问题,第一次调用erase过后,迭代器it就失效了。
因为,对一个已经失效的迭代器,再进行加加的时候代码就会产生不可预期的错误,是一个非法的操作

场景二:给vec容器中所有的偶数前面添加一个小于偶数值1的数字
代码实现如下:

vector<int> vec;
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
auto it = vec.begin();
	for (; it != vec.end(); ++it)
	{
		if (*it % 2 == 0)
		{
			vec.insert(it,*it-1);
			break;
		}
	}
}

有无break的效果同场景一一样。
通过以上两个场景的分析,我们可以总结出迭代器失效原因:

  1. .当容器调用erase方法后,当前位置到容器末尾元素的所有迭代器全部失效,如下图:
    在这里插入图片描述

  2. 当容器调用insert方法后,当前位置到容器末尾元素的所有迭代器全部失效,如下图:
    在这里插入图片描述

  3. insert来说,如果引起容器扩容。原来容器的所有迭代器就全部失效了,因为连内存都改变了

    扫描二维码关注公众号,回复: 10944513 查看本文章
  4. 不同容器的迭代器是不能进行比较运算的

二、如何解决迭代器失效问题

解决方案是,对插入/删除点的迭代器进行更新操作,去当前元素增加一个元素,或者将当前失效的迭代器删除,将新的iterator返回

例如,上述场景一,改进如下:

vector<int> vec;
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
auto it = vec.begin();
	while (it != vec.end())
	{
		if (*it % 2 == 0)
		{
			it = vec.erase(it);//对迭代器进行更新
		}
		else
		{
			++it;
		}
	}

运行结果如下:
在这里插入图片描述
场景二改进如下:

vector<int> vec;
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
auto it = vec.begin();
	for (; it != vec.end(); ++it)
	{
		if (*it % 2 == 0)
		{
			it = vec.insert(it, *it - 1);
			++it;
		}
	}
	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;

三、剖析迭代器失效底层原理

1、成员变量vector<T, Alloc>* _pVec

首先,我们要在iterator类的private里面添加一个成员变量vector<T, Alloc>* _pVec;
他的作用是让迭代器知道当前迭代器迭代的是哪个容器对象,所以给迭代器提供一个指向当前容器对象的指针
有了该指针过后,我们就可以去对!=,++和*运算符进行重载了。

(1)比较运算符重载
检查迭代器的有效性,如果为空,则代表该指针指向的容器是空的表示迭代器失效了。
还有两个不同对象的迭代器比较是没有意义的

bool operator!=(const iterator& it)const
		{
			if (_pVec == nullptr || _pVec != it._pVec)
			{
				throw"ierator incompatable!";
				return false;
			}
			return _ptr != it._ptr;
		}

(2)++运算符重载

void operator++()
		{
			//检查迭代器的有效性
			if (_pVec == nullptr)
			{
				throw"iterator invalid";
			}
			_ptr++;
		}

(3)解引用运算符

T& operator*()
		{
			//检查迭代器的有效性
			if (_pVec == nullptr)
			{
				throw"iterator invalid";
			}
			return *_ptr;
		}

2、底层Iterator_Base结构

容器迭代器失效增加代码,用链表记录用户获取的哪一个迭代器
(1)Iterator_Base结构的代码实现如下:

struct Iterator_Base
	{
		Iterator_Base(iterator* c = nullptr, Iterator_Base* n = nullptr)
			:_cur(c), _next(n) {}
		iterator* _cur;//维护了指向某一个迭代器的指针
		Iterator_Base* _next;//指向下一个迭代器对象的地址
	};
	Iterator_Base _head;//头结点

它的结构如图所示:
在这里插入图片描述
(2)iterator构造函数实现如下:

iterator(vector<T, Alloc>* pvec=null,T* ptr = nullptr)
			:_ptr(ptr), _pVec(pvec){}
		{
			Iterator_Base* itb =
				new Iterator_Base(this, _pVec->_head._next);
			_pVec->_head._next = itb;
		}

它的结构如图所示:
在这里插入图片描述

3、verfiy函数

pop_back中加入了verfiy,pop_back从当前末尾删除,verify检查有效性。在我们增加或删除后,把我们当前节点的地址到末尾的地址,全部进行检查,在存储的迭代器链表上进行遍历,哪一个迭代器指针指向的迭代器迭代元素的指针在检查范围内,就将相应迭代器指向容器的指针置为空,即为失效的迭代器。
具体实现如下:

void verify(T *first,T *last)
	{
		Iterator_Base* pre = &this->_head;
		Iterator_Base* it = this->_head._next;
		while (it != nullptr)
		{
			if (it->_cur->_ptr >= first&& it->_cur->_ptr <= last)
			{
				//迭代器失效,把itreator持有的容器指针置nullptr
				it->_cur->_pVec = nullptr;
				//删除当前迭代器节点,继续判断后面的迭代器节点是否失效
				pre->_next = it->_next;
				delete it;
				it = pre->_next;
			}
			else
			{
				pre = it;
				it = it->_next;
		}

图解如下:
在这里插入图片描述

4、自定义vector容器insert、erase方法实现

(1)自定义vector容器insert方法实现

iterator insert(iterator it, const T &val)
	{
		//1.这里我们未考虑扩容
		//2.还未考虑it._ptr指针合法性,假设它合法
		verify(it._ptr - 1, _last);
		T *p = _last;
		while (p > it._ptr)
		{
			_allocator.construct(p, *(p-1));
			_allocator.destroy(p - 1);
			p--;
		}
		_allocator.construct(p, val);
		_last++;
		return iterator(this, p);
	}

(2)自定义vector容器erase方法实现

iterator erase(iterator it)
	{
		verify(it._ptr - 1, _last);
		T *p = it._ptr;
		while (p < _last-1)
		{
			_allocator.destroy(p);
			_allocator.construct(p, *(p+1));
			p++;
		}
		_allocator.destroy(p);
		_last--;
		return iterator(this, it._ptr);
	}

四、源代码

template <typename T>
struct Allocator
{
	T* allocate(size_t size)//只负责内存开辟
	{
		return (T*)malloc(sizeof(T) * size);
	}
	void deallocate(void* p)//只负责内存释放
	{
		free(p);
	}
	void construct(T* p, const T& val)//已经开辟好的内存上,负责对象构造
	{
		new (p) T(val);//定位new,指定内存上构造val,T(val)拷贝构造
	}
	void destroy(T* p)//只负责对象析构
	{
		p->~T();//~T()代表了T类型的析构函数
	}
};

template <typename T, typename Alloc = Allocator<T>>
class vector//向量容器
{
public:
	vector(int size = 10)//构造
	{
		//_first = new T[size];
		_first = _allocator.allocate(size);
		_last = _first;
		_end = _first + size;
	}
	~vector()//析构
	{
		//delete[]_first;
		for (T* p = _first; p != _last; ++p)
		{
			_allocator.destroy(p);//把_first指针指向的数组的有效元素析构
		}
		_allocator.deallocate(_first);//释放堆上的数组内存
		_first = _last = _end = nullptr;
	}
	vector(const vector<T>& rhs)//拷贝构造
	{
		int size = rhs._end - rhs._first;//空间大小
		//_first = new T[size];
		_first = _allocator.allocate(size);
		int len = rhs._last - rhs._first;//有效元素
		for (int i = 0; i < len; ++i)
		{
			//_first[i] = rhs._first[i];
			_allocator.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
	}
	vector<T>& operator=(const vector<T>& rhs)//赋值运算符重载
	{
		if (this == &rhs)
		{
			return *this;
		}

		//delete[]_first;
		for (T* p = _first; p != _last; ++p)
		{
			_allocator.destory(p);//把_first指针指向的数组的有效元素析构
		}
		_allocator.deallocate(_first);//释放堆上的数组内存

		int size = rhs._end - rhs._first;//空间大小
		_first = _allocator.allocate(size);
		int len = rhs._last - rhs._first;//有效元素
		for (int i = 0; i < len; ++i)
		{
			_allocator.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
		return *this;
	}
	void push_back(const T& val)//尾插
	{
		if (full())
		{
			expand();
		}
		//*_last++ = val;
		_allocator.construct(_last, val);//_last指针指向的内存构造一个值为val的对象
		_last++;
	}
	void pop_back()//尾删
	{
		if (empty()) return;
		//检查是否失效
		//如果是使用erase(it),检查范围改变verify(it._ptr, _last);
		//如果是使用insert(it),检查范围改变verify(it._ptr, _last);
		verify(_last - 1, _last);
		--_last;
		_allocator.destroy(_last);
	}
	T back()const//返回容器末尾元素值
	{
		return *(_last - 1);
	}
	bool full()const
	{
		return _last == _end;
	}
	bool empty()const
	{
		return _first == _last;
	}
	int size()const//返回容器中元素个数
	{
		return _last - _first;
	}
	T& operator[](int index)
	{
		if (index < 0 || index >= size())
		{
			throw "OutOfRangeException";
		}
		return _first[index];
	}
	//迭代器一般实现成容器的嵌套类型
	class iterator
	{
	public:
		iterator(vector<T, Alloc>* pvec=null,T* ptr = nullptr)
			:_ptr(ptr), _pVec(pvec){}
		{
			Iterator_Base* itb =
				new Iterator_Base(this, _pVec->_head._next);
			_pVec->_head._next = itb;
		}
		bool operator!=(const iterator& it)const
		{
			//检查迭代器的有效性,如果为空,则代表该指针指向的容器是空的表示迭代器失效了。
			//还有两个不同对象的迭代器比较是没有意义的
			if (_pVec == nullptr || _pVec != it._pVec)
			{
				throw"ierator incompatable!";
				return false;
			}
			return _ptr != it._ptr;
		}
		void operator++()
		{
			//检查迭代器的有效性
			if (_pVec == nullptr)
			{
				throw"iterator invalid";
			}
			_ptr++;
		}
		T& operator*()
		{
			//检查迭代器的有效性
			if (_pVec == nullptr)
			{
				throw"iterator invalid";
			}
			return *_ptr;
		}
		const T& operator*()const
		{
			//检查迭代器的有效性
			if (_pVec == nullptr)
			{
				throw"iterator invalid";
			}
			return *_ptr;
		}
	private:
		T* _ptr;
		vector<T, Alloc>* _pVec;
		//让迭代器知道当前迭代器迭代的是哪个容器对象,所以给迭代器提供一个指向当前容器对象的指针
	};
	iterator begin()
	{
		return iterator(_first);
	}
	iterator end()
	{
		return iterator(_last);
	}
	void verify(T *first,T *last)
	{
		Iterator_Base* pre = &this->_head;
		Iterator_Base* it = this->_head._next;
		while (it != nullptr)
		{
			if (it->_cur->_ptr >= first&& it->_cur->_ptr <= last)
			{
				//迭代器失效,把itreator持有的容器指针置nullptr
				it->_cur->_pVec = nullptr;
				//删除当前迭代器节点,继续判断后面的迭代器节点是否失效
				pre->_next = it->_next;
				delete it;
				it = pre->_next;
			}
			else
			{
				pre = it;
				it = it->_next;
		}
	//自定义vector容器insert方法实现
	iterator insert(iterator it, const T &val)
	{
		//1.这里我们未考虑扩容
		//2.还未考虑it._ptr指针合法性,假设它合法
		verify(it._ptr - 1, _last);
		T *p = _last;
		while (p > it._ptr)
		{
			_allocator.construct(p, *(p-1));
			_allocator.destroy(p - 1);
			p--;
		}
		_allocator.construct(p, val);
		_last++;
		return iterator(this, p);
	}

	//自定义vector容器erase方法实现
	iterator erase(iterator it)
	{
		verify(it._ptr - 1, _last);
		T *p = it._ptr;
		while (p < _last-1)
		{
			_allocator.destroy(p);
			_allocator.construct(p, *(p+1));
			p++;
		}
		_allocator.destroy(p);
		_last--;
		return iterator(this, it._ptr);
	}

private:
	T* _first;//起始数组位置
	T* _last;//指向最后一个有效元素后继位置
	T* _end;//指向数组空间的后继位置
	Alloc _allocator;//定义容器的空间配置器对象

	//容器迭代器失效增加代码,用链表记录用户获取的哪一个迭代器
	struct Iterator_Base
	{
		Iterator_Base(iterator* c = nullptr, Iterator_Base* n = nullptr)
			:_cur(c), _next(n) {}
		iterator* _cur;//维护了指向某一个迭代器的指针
		Iterator_Base* _next;//指向下一个迭代器对象的地址
	};
	Iterator_Base _head;//头结点

	void expand()//扩容
	{
		int size = _end - _first;
		//T *ptmp = new T[2*size];
		T* ptmp = _allocator.allocate(2 * size);
		for (int i = 0; i < size; ++i)
		{
			_allocator.construct(ptmp + i, _first[i]);
			//ptmp[i] = _first[i];
		}
		//delete[]_first;
		for (T* p = _first; p != _last; ++p)
		{
			_allocator.destroy(p);
		}
		_allocator.deallocate(_first);
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;
	}
};
发布了98 篇原创文章 · 获赞 9 · 访问量 3667

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105149550