容器的迭代器

在这里插入图片描述

迭代器设置成容器的嵌套类型(public下实现),不同的容器有其特定的迭代器。需要注意迭代器类型需要哪些成员变量取决于容器底层的数据结构需要怎样去进行遍历,并且遍历容器底层元素的主要实现代码都封装在迭代器提供的前置++运算符重载函数中

迭代器功能:提供一种统一的使用方式以透明地访问容器内部的元素的值(不同的容器底层的数据结构是不同的,但是通过迭代器我们不需要知道容器的底层数据结构,就可以采用统一的方式访问不同容器内部元素的值)(既然是访问底层元素值,那本质上还是通过指针实现的!!!)

String内部实现的迭代器,在其内部提供的方法:

​ bool operator!=(const iterator& it)

​ void operator++() // 前置++

​ char& operator*()const // 迭代器解引用

String提供的方法:

​ iterator begin()返回的是容器底层首元素的迭代器的表示

​ iterator end() 返回的是容器最后一个有效元素的后继位置的迭代器

内部实现了迭代器的String类(请重点关注iterator的实现)

class String 
{
public:
	String(const char* str=nullptr) 
	{
		if (nullptr != str)
		{
			_pstr = new char[strlen(str) + 1];
			strcpy(_pstr, str);// 别忘了拷贝啊。。。。
		}
		else 
		{// 避免构造对象后底层的_pstr可能为nullptr的情况
			_pstr = new char[1];
			*_pstr = '\0';
		}
			 
	}
	~String() 
	{
		delete[]_pstr;
		_pstr = nullptr;
	}
	// 拷贝构造列表
	String(const String& rhs)// 需要进行深拷贝
	{
			 
		_pstr = new char[strlen(rhs._pstr) + 1];
		strcpy(_pstr, rhs._pstr);
	}

	// 赋值运算符重载函数,需要进行深拷贝
	String& operator=(const String& rhs)  
	{
		if (this == &rhs)
			return *this;

		delete[]_pstr;
		_pstr = new char[strlen(rhs._pstr) + 1];
		strcpy(_pstr, rhs._pstr);
	}

	bool operator>(const String& rhs) const 
	{
		return strcmp(_pstr, rhs._pstr)>0;
	}

	bool operator<(const String& rhs) const
	{
		return strcmp(_pstr, rhs._pstr) < 0;
	}

	bool operator==(const String& rhs) const
	{
		return strcmp(_pstr, rhs._pstr) == 0;
	}

	// 返回字符串有效长度
	int length()const 
	{
		return strlen(_pstr);
	}

	// 返回引用,支持对字符串内容进行修改
	char& operator[](/* String* this, */int index)
	{
		return _pstr[index];
	}

	// 常对象通过索引和[]运算符返回的字符不能被修改
	const char& operator[](/* const String* this, */int index) const
	{
		return _pstr[index];
	}

	char* c_str() 
	{
		return _pstr;
	}
//
	// 在String 类内部定义iterator类
	class iterator 
	{
	public:
		iterator(char*p=nullptr) 
			:_p(p)
		{}
		
		bool operator!=(const iterator& it) 
		{
			return _p != it._p;
		}
		
        // 为常对象和普通对象提供不同的*运算符重载函数
		char& operator*() 
		{
			return *_p;
		}
        
        const char& operator*()const 
		{
			return *_p;
		}
        

		// 前置++
		void operator++() 
		{
			++_p;
		}
		
	private:
		char* _p;
	};
    // 容器内部提供begin()和end()方法
	iterator begin() { return iterator(_pstr); }
	iterator end() { return iterator(_pstr + strlen(_pstr)); }
//    
private:
	char* _pstr;
	 
	friend String operator+(const String& lhs, const String& rhs);
	friend ostream& operator<<(ostream& out, const String& str);
};

// 全局+运算符重载,对于String来说,+执行的是拼接操作
String operator+(const String& lhs, const String& rhs) 
{
	String tmp; 
	tmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(tmp._pstr, lhs._pstr);
	strcat(tmp._pstr, rhs._pstr);
	return tmp;
}

// 输出运算符重载函数
ostream& operator<<(ostream& out, const String& str) 
{
	out << str._pstr;
	return out;
}

使用迭代器访问容器元素

下面给出了两种常用的使用迭代器遍历容器元素的方式

int main() 
{
	String str1 = "hello world";
    
    // iterator是String内部实现的类,在外部定义对象时需要加上类名作用域
	String::iterator it = str1.begin();// 返回容器首元素的一个迭代器表示
	//  auto it = str1.begin();
	for (; it != str1.end(); ++it) 
	{
		cout << *it;
	}
	cout << endl;

	// 使用foreach的方式来遍历str1,底层是通过迭代器实现的
	for (char ch : str1) 
	{
		cout << ch;

	}
	cout << endl;
    return 0;
}
  • c++11中提供使用foreach的方法来遍历容器(底层还是通过迭代器来实现的,容器要提供begin和end方法)

  • c++11提供的auto 关键字,自动根据=右边的类型推导其左边的数据类型


vector容器的迭代器实现(无迭代器失效检查)

对于[]运算符重载函数,并不是每个容器都有,因为vector底层是数组(线性表),元素在内存上是连续存储的,所以可以提供[],对于底层是链表或者其他数据结构的容器,就无法使用[]。

// 定义空间配置器
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);// 在p指向的内存上拷贝构造对象
	}

	void destroy(T* p) // 析构对象
	{
		p->~T();
	}
};


template<typename T, typename Alloc = Allocator<T>> // 类型参数列表,默认配置器的类型为Allocator<T>
class Vector
{
public:
	// 可以通过构造函数传入空间配置器对象,简单起见,这里就不传入了
	//Vector(size_t size = 10, const Alloc& alloc){}
	Vector(size_t size = 10)
	{
		_first = _alloc.allocate(size);// 开辟一块数组内存空间
		_last = _first;
		_end = _first + size;
	}
	~Vector()
	{
		// 先析构容器中的有效元素
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}

		// 再释放容器内存
		_alloc.deallocate(_first);

		// 避免野指针
		_first = _last = _end = nullptr;
	}

	// 因为成员变量会访问外部资源,所以拷贝构造和赋值重载需要进行深拷贝
	Vector(const Vector<T>& rhs)
	{
		int size = rhs._end - rhs._first;
		int len = rhs._last - rhs._first;
		_first = _alloc.allocate(size);

		for (int i = 0; i < len; ++i)
		{
			_alloc.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;

	}

	// 返回引用类型,便于连续赋值
	Vector<T>& operator=(const Vector<T>& rhs)
	{
		// 防止自赋值
		if (this == &rhs)
			return *this;

		// 先析构原容器的有效元素,并释放原容器内存
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}
		_alloc.deallocate(_first);

		int size = rhs._end - rhs._first;
		int len = rhs._last - rhs._first;
		_first = _alloc.allocate(size);

		for (int i = 0; i < len; ++i)
		{
			_alloc.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;

		return *this;

	}

	// 从容器末尾添加元素(在_last指针指向的内存构造一个对象)
	void push_back(const T& val)
	{
		if (full())
			expand();
		//_last先使用后++,在_last所指向的内存上,通过val拷贝构造新对象
		_alloc.construct(_last++, val);
	}

	// 从容器末尾删除元素
	void pop_back()
	{
		if (empty())
			throw "vector is empty!";
		--_last;// _last指向有效元素的后继位置,所以先--
		_alloc.destroy(_last);// 析构末尾有效元素

	}

	// []运算符重载函数
	T& operator[](int index) 
	{
		if (index < 0 || index >= size())
			throw "OutOfRangeException";
		return _first[index]; 
	}

	// 返回容器有效元素个数
	int size()const { return _last - _first; }

	// 获取容器末尾元素
	T back()const
	{
		return *(_last - 1);
	}

	// 判断容器是否已满
	bool full()const
	{
		return _last == _end;
	}

	// 判断容器是否为空
	bool empty()const
	{
		return _first == _last;
	}

	class iterator
	{
	public:
		iterator(T* p=nullptr) 
			:_p(p)
		{}

		bool operator!=(const iterator& it) 
		{
			return _p != it._p;
		}

		// 前置++
		void operator++() 
		{
			_p++;
		}

		T& operator*() { return *_p; }
		const T& operator*()const { return *_p; }
	private:
		T* _p;		// 指向容器底层元素的指针
	};

	iterator begin() { return iterator(_first); }
	iterator end() { return iterator(_last); }

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

	void expand()
	{
		size_t size = _end - _first;
		T* ptmp = _alloc.allocate(2 * size);
		for (size_t i = 0; i < size; ++i)
		{
			_alloc.construct(ptmp + i, _first[i]);
		}

		// 析构原来的有效元素
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}
		_alloc.deallocate(_first);
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;
	}

};


容器的迭代器失效问题(重点)

  1. 迭代器为什么会失效?

当vector容器调用erase方法后,当前位置到容器末尾元素的所有的迭代器全部失效了

当vector容器调用insert方法后, 当前位置到容器末尾元素的所有的迭代器全部失效了

对于insert方法来说,如果引起vector容器内存扩容,原来容器的所有的迭代器就全部失效了
(起始扩容实际是申请了一块更大的内存,把原来内存的元素拷贝到新内存,然后释放掉原来的内存)

在这里插入图片描述

会引起迭代器失效的代码

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) 
    {
        //这里迭代器在第一次执行erase之后,删除位置以及之后的所有位置的迭代器都失效了
        vec.erase(it);
        //break;
    }
}

// 在容器中所有的偶数前面添加一个小于偶数值1的数
auto it = vec.begin();
for (; it != vec.end(); ++it)
{
    if (*it % 2 == 0)
    {
        // 这里迭代器在第一次执行insert之后,插入位置以及之后的所有位置的迭代器都失效了
        // 如果insert引起了扩容,那么所有位置的迭代器都失效了
        vec.insert(it,*it-1);
        //break;
    }
}

在这里插入图片描述

  1. 迭代器失效了以后,问题该怎么解决?

对插入/删除点的迭代器进行更新操作(当涉及到使用迭代器进行连续的删除/插入,要记得在删除/插入点进行更新)

int main()
{
	vector<int> vec;
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100 + 1);
	}

	for (int v : vec) 
	{
		cout << v << " ";
	}
	cout << endl;

	auto it = vec.begin();
	// 把容器中所有的偶数全部删除
	while(it != vec.end())
	{
		if (*it % 2 == 0)
		{
            // 删除以后返回一个合法的迭代器
            // 删除以后,后面的元素会往前移,所以不需要++it
			it=vec.erase(it);
		}
		else 
		{
			++it;
		}
	}
	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;
    
    return 0;
}

在这里插入图片描述

int main()
{
	vector<int> vec;
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100 + 1);
	}

	for (int v : vec) 
	{
		cout << v << " ";
	}
	cout << endl;

	auto it = vec.begin();
	// 在容器中所有的偶数前面添加一个小于偶数值1的数
	for (; it != vec.end(); ++it)
	{
		if (*it % 2 == 0)
		{
         	// 返回插入位置的迭代器,原来的元素后移了   
			it=vec.insert(it, *it - 1);
            // 插入后一定要执行++it
			++it;
		}
	}
	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;
    
    return 0;
}

在这里插入图片描述

3. 代码部分是如何实现迭代器的失效检查的?

vector类的private域上定义了用来完成容器失效的代码

class Vector{
	....
private:
    // 容器迭代失效代码
    struct Iterator_Base 
    {
        Iterator_Base(iterator* cur = nullptr, Iterator_Base* next = nullptr)
            :_cur(cur)
            ,_next(next)
            {}
        iterator* _cur;			// 数据域,指向迭代器对象
        Iterator_Base* _next;	// 指针域,指向下一个Iterator_Base节点
    };
    Iterator_Base _head;		// 头节点,同时也是Vector容器私有域的一个成员对象
    ......
}
 		 

从上面的代码可以看出,vector容器底层使用一个链表来维护迭代器 。每构造一个迭代器对象,都会产生一个新的 Iterator_Base节点,并使用头插法插入链表

在这里插入图片描述

vector容器中iterator的实现部分:

class iterator
	{
	public:
		friend class Vector<T, Alloc>; // 设置友元类
		iterator(Vector<T, Alloc>* pvec, T* ptr = nullptr)
			:_pvec(pvec)
			,_ptr(ptr)
		{
            /*
            没构造一个迭代器,都会生成新的Iterator_Base节点,并插入链表中
            这里this指向新生成的迭代器对象
            采用头插法的方式将新的Iterator_Base节点插入链表
            */
			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 "iterator invalid";
			}
			else 
			{
				return _ptr != it._ptr;
			}
		}
		void operator++()// 前置++
		{
			// 检查迭代器的有效性
			if (_pvec == nullptr) 
			{
				throw "iterator invalid";
			}
			_ptr++;
		}
		const T& operator*() const
		{// *_ptr不是该函数的局部变量,所以可以设置成返回引用的形式
			return *_ptr;
		}
		T& operator*()
		{// *_ptr不是该函数的局部变量,所以可以设置成返回引用的形式
			return *_ptr;
		}

	private:
		T* _ptr;  					// 容器底层数组元素的首地址
		Vector<T, Alloc>* _pvec; 	// 指向容器对象的指针(确保迭代器知道自己正在遍历哪个容器对象)
		
	};
  • 不同容器(指的是容器对象)的迭代器是不能进行比较的,所以我们需要判断迭代器是否指向同一个容器,因此我们需要给迭代器添加一个指向容器对象的指针,作为成员变量, 这样迭代器对象就可以知道目前指向哪个容器对象
  • 同时,在Iterator中提供的operator!=以及operator++()运算符重载函数中都进行了迭代器失效检查,但迭代器底层的容器对象指针为nullptr时,说明该迭代器已经失效,不再起作用;此外,operator!=中,只有迭代器的容器对象指针都指向同一个容器对象,迭代器才有比较的意义

vector类public域下verify方法的实现

作用:检查迭代器的指向是否在失效范围内[first, last],如果在失效范围内,那么使迭代器底层指针在[first, last]范围内的迭代器失效

所谓的失效操作,其实就是将范围内的迭代器的容器对象指针置为nullptr,并从链表上删去

迭代器的容器对象指针置空后,就无法进行迭代器的运算

 
	void verify(T*first, T*last) 
	{
		Iterator_Base* pre = &this->_head;
		Iterator_Base* it = this->_head._next;
		
        // 从头遍历,vector底层维护迭代器对象的链表
        while (it != nullptr) 
		{
            // 所谓的失效操作,其实就是将范围内的迭代器的容器对象指针置为nullptr,并从链表上删去
			if (it->_cur->_ptr > first && it->_cur->_ptr <= last) 
			{
				// 迭代器失效,把迭代器所持有的容器对象指针置为nullptr
				it->_cur->_pvec = nullptr;
				// 删除当前迭代器节点,继续判断后面的迭代器节点是否有效
				pre->_next = it->_next;
				delete it;
				it = pre->_next;// 删完节点后,要继续向后遍历
			}
			else 
			{
				pre = it;
				it = it->_next;
			}
		}
	}

自定义vector容器的insert和erase方法,结合迭代器失效问题

在这里插入图片描述

如上图所示,当调用vector的insert方法插入新元素时,首先插入位置到_last的迭代器对象都会失效(通过verify()方法实现), 所谓的失效指的是:迭代器底层的容器对象指针pvec为nullptr,并且数据域指向该迭代器的节点将从容器对象维护的链表中删除

插入时,需要将元素往后移动,这里实际执行的操作是:利用前一个对象构造新的对象,并析构前一个对象

insert方法返回新的有效的迭代器,迭代器的容器对象指针指向当前容器,并且生成相应的节点通过头插方式插入容器对象维护的链表中,迭代器底层的指向容器元素的指针则指向插入位置

insert方法的实现

// 在迭代器所在的位置插入新元素
iterator insert(iterator& it, const T& val) 
{
    /*
		方便起见,不考虑以下情况:
			(1)insert导致扩容
			(2)不检查it底层指针的合法性-->即默认都合法
     */

    // 从当前位置开始,所有元素向后移一位
    // 需要利用前一个对象在后移到的位置构造对象,并析构前一个对象
    verify(it._p - 1, _last);
    T* p = _last;
    while (p > it._p) 
    {
        _alloc.construct(p, *(p - 1));
        --p;
        _alloc.destroy(p);

    }
    _alloc.construct(p, val);
    _last++;// 一定记得啊!!

    return iterator(this, p);

}

// 删除迭代器所指向位置的元素
iterator erase(iterator& it) 
{
    // 使[it._p, _last]范围内迭代器失效
    verify(it._p - 1, _last);

    // 当前位置之后的所有元素都向前移一位(也是拷贝构造+析构)
    T* p = it._p;
    while (p < _last - 1) 
    {
        _alloc.destroy(p);
        _alloc.construct(p, *(p + 1));
        p++;
    }
    _alloc.destroy(p);
    _last--;// 一定记得啊!!
    return iterator(this, it._p);
}

调用erase方法时,从删除位置到_last的所有迭代器都将失效;

删除后,元素要往前移动,实际指向的操作是析构当前位置的对象,然后通过后面位置的对象在当前位置构造新对象;

和insert方法一样要返回新的有效的迭代器。

erase方法的实现

iterator erase(iterator& it) 
	{
		verify(it._p - 1, _last);

		// 当前位置之后的所有元素都向前移一位(也是拷贝构造+析构)
		T* p = it._p;
		while (p < _last - 1) 
		{
			_alloc.destroy(p);
			_alloc.construct(p, *(p + 1));
			p++;
		}
		_alloc.destroy(p);
		_last--;// 一定记得啊!!
		return iterator(this, it._p);
	}

加入了迭代器失效检查并且实现了insert和erase方法的vector容器的完整代码

// 定义空间配置器
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);// 在p指向的内存上拷贝构造对象
	}

	void destroy(T* p) // 析构对象
	{
		p->~T();
	}
};


template<typename T, typename Alloc = Allocator<T>> // 类型参数列表,默认配置器的类型为Allocator<T>
class Vector
{
public:
	// 可以通过构造函数传入空间配置器对象,简单起见,这里就不传入了
	//Vector(size_t size = 10, const Alloc& alloc){}
	Vector(size_t size = 10)
	{
		_first = _alloc.allocate(size);// 开辟一块数组内存空间
		_last = _first;
		_end = _first + size;
	}
	~Vector()
	{
		// 先析构容器中的有效元素
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}

		// 再释放容器内存
		_alloc.deallocate(_first);

		// 避免野指针
		_first = _last = _end = nullptr;
	}

	// 因为成员变量会访问外部资源,所以拷贝构造和赋值重载需要进行深拷贝
	Vector(const Vector<T>& rhs)
	{
		int size = rhs._end - rhs._first;
		int len = rhs._last - rhs._first;
		_first = _alloc.allocate(size);

		for (int i = 0; i < len; ++i)
		{
			_alloc.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;

	}

	// 返回引用类型,便于连续赋值
	Vector<T>& operator=(const Vector<T>& rhs)
	{
		// 防止自赋值
		if (this == &rhs)
			return *this;

		// 先析构原容器的有效元素,并释放原容器内存
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}
		_alloc.deallocate(_first);

		int size = rhs._end - rhs._first;
		int len = rhs._last - rhs._first;
		_first = _alloc.allocate(size);

		for (int i = 0; i < len; ++i)
		{
			_alloc.construct(_first + i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;

		return *this;

	}

	// 从容器末尾添加元素(在_last指针指向的内存构造一个对象)
	void push_back(const T& val)
	{
		if (full())
			expand();
		//_last先使用后++,在_last所指向的内存上,通过val拷贝构造新对象
		_alloc.construct(_last++, val);
	}

	// 从容器末尾删除元素
	void pop_back()
	{
		if (empty())
			throw "vector is empty!";
		verify(_last - 1, _last);
		--_last;// _last指向有效元素的后继位置,所以先--
		_alloc.destroy(_last);// 析构末尾有效元素

	}


	// []运算符重载函数
	T& operator[](int index)
	{
		if (index < 0 || index >= size())
			throw "OutOfRangeException";
		return _first[index];
	}

	// 返回容器有效元素个数
	int size()const { return _last - _first; }

	// 获取容器末尾元素
	T back()const
	{
		return *(_last - 1);
	}

	// 判断容器是否已满
	bool full()const
	{
		return _last == _end;
	}

	// 判断容器是否为空
	bool empty()const
	{
		return _first == _last;
	}

	// vector容器内部实现迭代器--->容器的嵌套类型
	class iterator
	{
	public:
		friend class Vector<T, Alloc>;
		iterator(Vector<T, Alloc>* pvec, T* p = nullptr)
			:_pvec(pvec),
			_p(p)
		{
			/*
			构造迭代器对象的时候,也构造Iterator_Base节点,并已头插法方式插入链表中
			*/
			// 这里的this指的是当前新构造的迭代器自身
			Iterator_Base* pit = new Iterator_Base(this, _pvec->_head._next);
			_pvec->_head._next = pit;
		}
		 
		bool operator!=(const iterator& it)
		{
			// _pvec为nullptr说明迭代器失效
			// 不同容器对象的迭代器没有比较的意义
			if (_pvec == nullptr || _pvec != it._pvec) 
			{
				throw "iterator invalid";
			}
			return _p != it._p;
		}

		// 前置++
		void operator++()
		{
			if (_pvec == nullptr) 
			{
				throw "iterator invalid";
			}
			_p++;
		}

		T& operator*() { return *_p; }
		const T& operator*()const { return *_p; }
	private:
		T* _p;						// 指向容器底层元素的指针
		Vector<T, Alloc>* _pvec;	// 指向容器对象的指针
	};

	// 这里的this指向容器对象本身
	iterator begin() { return iterator(this, _first); }
	iterator end() { return iterator(this, _last); }

	// 在迭代器所在的位置插入新元素
	iterator insert(iterator& it, const T& val) 
	{
		/*
		方便起见,不考虑以下情况:
			(1)insert导致扩容
			(2)不检查it底层指针的合法性-->即默认都合法
		*/

		// 从当前位置开始,所有元素向后移一位
		// 需要利用前一个对象在后移到的位置构造对象,并析构前一个对象
		verify(it._p - 1, _last);
		T* p = _last;
		while (p > it._p) 
		{
			_alloc.construct(p, *(p - 1));
			--p;
			_alloc.destroy(p);
			 
		}
		_alloc.construct(p, val);
		_last++;// 一定记得啊!!

		return iterator(this, p);
		
	}

	iterator erase(iterator& it) 
	{
		verify(it._p - 1, _last);

		// 当前位置之后的所有元素都向前移一位(也是拷贝构造+析构)
		T* p = it._p;
		while (p < _last - 1) 
		{
			_alloc.destroy(p);
			_alloc.construct(p, *(p + 1));
			p++;
		}
		_alloc.destroy(p);
		_last--;// 一定记得啊!!
		return iterator(this, it._p);
	}

	// 检查迭代器失效,使得底层指针指向地址在(first,last]范围内的迭代器失效 
	void verify(T* first, T* last) 
	{// 遍历链表
		Iterator_Base* pre = &(this->_head);
		Iterator_Base* cur = this->_head._next;
		while (cur != nullptr)
		{
			if (cur->_cur->_p > first && cur->_cur->_p <= last) 
			{	/*
				迭代器失效操作:
					1)迭代器的容器对象指针置为nullptr, 
					2)将数据域指向该迭代器的节点从链表删除
				*/
				cur->_cur->_pvec = nullptr;
				pre->_next = cur->_next;
				delete cur;
				cur = pre->_next; // 节点指针要移到下一个节点继续开始遍历,这个不能漏写
			}
			else 
			{
				pre = cur;
				cur = cur->_next;
			}
		}

	}

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

	// 容器对象底层通过一个链表来维护由该容器对象创建的迭代器
	struct Iterator_Base 
	{
		Iterator_Base(iterator* cur=nullptr, Iterator_Base* next=nullptr)
			:_cur(cur),
			_next(next)
		{}
		iterator* _cur;				// 数据域,指向迭代器对象
		Iterator_Base* _next;		// 指针域,指向下一个Iterator_Base节点
	};

	Iterator_Base _head;		// 链表头节点,是容器的成员对象

	 
	void expand()
	{
		size_t size = _end - _first;
		T* ptmp = _alloc.allocate(2 * size);
		for (size_t i = 0; i < size; ++i)
		{
			_alloc.construct(ptmp + i, _first[i]);
		}

		// 析构原来的有效元素
		for (T* p = _first; p != _last; ++p)
		{
			_alloc.destroy(p);
		}
		_alloc.deallocate(_first);
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;
	}

};

测试1

Vector<int> vec;
for (int i = 0; i < 20; ++i)
{
    vec.push_back(rand() % 100 + 1);
}
auto it1 = vec.end();
vec.pop_back(); // verify(_last-1, _last)
auto it2 = vec.end();
cout << (it1 != it2) << endl;

调用pop_back()后执行verify(),迭代器it1失效了(容器对象指针被置空),所以执行operator!=将会弹出异常

在这里插入图片描述

测试2:在容器所有偶数元素前添加减1的新元素

Vector<int> vec(200); // 因为我们insert方法不考虑扩容,所以我们底层数组容量设置的大一点
auto it = vec.begin();
for (int i = 0; i < 20; ++i)
{
    vec.push_back(rand() % 100 + 1);
}

for (; it != vec.end(); ++it)
{
    if (*it % 2 == 0)
    {
        // 返回iterator进行更新,如果没有更新迭代器将抛出异常,因为原来的迭代器已经失效
        it = vec.insert(it, *it - 1);
        ++it;
    }
}
for (int v : vec)
{
    cout << v << " ";
}

在这里插入图片描述

测试3:删除容器所有的偶数元素

Vector<int> vec(200); // 因为我们insert方法不考虑扩容,所以我们底层数组容量设置的大一点
auto it = vec.begin();
for (int i = 0; i < 20; ++i)
{
    vec.push_back(rand() % 100 + 1);
}
while (it != vec.end())
{
    if (*it % 2 == 0)
    {
        // 返回iterator进行更新,如果没有更新迭代器将抛出异常,因为原来的迭代器已经失效
        it = vec.erase(it);
    }
    else
    {
        ++it;
    }

}
for (int v : vec)
{
    cout << v << " ";
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下酒番陪绅士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值