STL_vector实现及干货以及vector迭代器失效问题

vector迭代器失效:结果未定义

场景1:insert

你想在pos位置插入一个值

cpp 复制代码
typedef T* iterator;
void insert(iterator pos,const T& val)
{
	assert(pos>=_start);
	assert(pos<=_finish);
	//空间不够扩容
	if(_finish=_endofstorage)
	{
		reverse(capacity()==0?4:capacity()*2);
	}
	iterator cur=_finish;
	while(cur>=pos)
	{
		*cur=*(cur-1);
		cur--;
	}
	*cur=T(val);
	_finish++;
}

如果你用上面的函数用的次数多了,当它扩容的时候,你去调试,会发现pos位置变成随机值了,因为扩容可能会遇到异地扩容,当异地扩容的时候,原本的空间会被释放,把数据挪移到另外的空间,正确的写法应该是下面这种

cpp 复制代码
typedef T* iterator;
//返回插入数据的位置
iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start && pos <= _finish);
	if (size() == capacity())
	{
		size_t n = pos - _start;
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		pos = _start + n;
	}
	iterator cur = _finish;
	while (cur != pos)
	{
		*cur = *(cur - 1);
		cur--;
	}
	*pos = val;
	_finish++;
	return pos;
}

场景2:erase

cpp 复制代码
void erase(iterator pos)
{
	assert(pos>=_start&&pos<=_finish);
	iterator cur=pos+1;
	while(cur<_finish)
	{
		*(cur-1)=*cur;
		cur++;
	}
	_finish--;
}

由于pos位置删除完了以后,数据会发生改变,或者pos位置直接会没有有效数据,比如说尾删,删除了以后pos位置的数据非法,一般来说,我们不会再去用删除数据后的pos,但有一个场景下会出事,比如说连续删除

cpp 复制代码
//假设你要删除一个vector里所有的偶数
int main()
{
	vector<int> vec;
	iterator it=vec.begin();
	while(it!=vec.end())
	{
		if(*it%2==0)
		{
			erase(it);
		}
		++it;
	}
	return 0;
}

如果你真的这样写了,你会发现偶数可能删不干净,甚至出现越界的问题,所以我们一般认为只要删除,迭代器就失效,但官方的stl库给出了一个解决方法,如下图,大概意思是当erase删除后,会返回删除数据的下一个位置

cpp 复制代码
typedef T* iterator;
iterator erase(iterator pos)
{
	assert(size() != 0);
	assert(pos >= _start && pos <= _finish);
	iterator cur = pos + 1;
	while (cur != _finish)
	{
		*(cur - 1) = *cur;
		cur++;
	}
	_finish--;
	return pos;
}

vector的六个成员函数

构造函数

无参构造函数

刚开始如果没说有数据的话,我们一般不开空间。

cpp 复制代码
vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{}

用n个val构造

cpp 复制代码
vector(size_t n, const T& val = T())
{
	_start = new T[n];
	_finish = _start + n;
	_endofstorage = _start + n;
	iterator cur = _start;
	while (cur != _endofstorage)
	{
		*cur = val;
		cur++;
	}
}

我们一般会重载一个下面的版本,因为如果vector遇到整型,而我们又有区间构造和n个val构造的函数,编译器会优先选择区间构造,因为无符号整型跟你传的整型值没有说非常匹配,所以我们可以把size_t传参类型改成int类型重载一份,这样编译器遇到整型构造的时候就不会去选择区间构造了。

cpp 复制代码
vector(int n, const T& val = T())
{
	_start = new T[n];
	_finish = _start + n;
	_endofstorage = _start + n;
	iterator cur = _start;
	while (cur != _endofstorage)
	{
		*cur = val;
		cur++;
	}
}

区间构造

一定要用模板,因为你不知道需要传的区间的类型

cpp 复制代码
template<class InputIterator>
vector(InputIterator begin, InputIterator end)
	:_start(nullptr)
	,_finish(nullptr)
	,_endofstorage(nullptr)
{
	while (begin != end)
	{
		push_back(*begin);
		begin++;
	}
}

拷贝构造

注意,在扩容的时候,一定不能用memcpy或者memmove这种函数,因为这种函数是逐字节逐字节拷贝的,会发生析构两次或者写其中一个变量导致另外一个变量变化的问题,在这边为了解决这个问题,我们可以用赋值的方法,如果有自定义类型,它会自动去调用自定义类型的赋值重载函数。

cpp 复制代码
vector(const vector<T>& v)
{
	_start = new T[v.capacity()];
	_finish = _start + v.size();
	_endofstorage = _start + v.capacity();
	iterator cur = _start;
	for (size_t i = 0; i < v.size(); i++)
	{
		*cur = v[i];
		cur++;
	}
}

赋值重载

下面的重载=的函数传参的时候是有调用拷贝构造产生新的vector的,我们为了可以减少程序资源消耗,可以直接把两个变量的指针交换了,可能有的人会问,这个传参是临时变量啊,出了重载函数不就会销毁了吗,但其实这个变量是new出来的,我们只要不手动释放,就会一直留在那边,编译器不会帮我们释放。

cpp 复制代码
void swap(vector<T> v)
{
	_start = v._start;
	_finish = v._finish;
	_endofstorage = v._endofstorage;
}
vector<T>& operator=(const vector<T> v)
{
	swap(v);
}

析构函数

三个iterator变量最好全部都置空一下,保持良好的写代码习惯

cpp 复制代码
~vector()
{
	delete[] _start;
	_start = _finish = _endofstorage = nullptr;
}

取地址操作符重载函数

这两个函数一般不会自己实现的,都是由编译器自行产生的

cpp 复制代码
vector<T>* operator&()
{
	return this;
}
const vector<T>* operator&() const
{
	return this;
}

增删查改

push_back可以复用insert

cpp 复制代码
iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start && pos <= _finish);
	if (size() == capacity())
	{
		size_t n = pos - _start;
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		pos = _start + n;
	}
	iterator cur = _finish;
	while (cur != pos)
	{
		*cur = *(cur - 1);
		cur--;
	}
	*pos = val;
	_finish++;
	return pos;
}
void push_back(const T& val)
{
	/*if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}
	*_finish = val;
	_finish++;*/
	insert(end(), val);
}

和增同理,pop_back同样可以复用erase

cpp 复制代码
iterator erase(iterator pos)
{
	assert(size() != 0);
	assert(pos >= _start && pos <= _finish);
	iterator cur = pos + 1;
	while (cur != _finish)
	{
		*(cur - 1) = *cur;
		cur++;
	}
	_finish--;
	return pos;
}
void pop_back()
{
	assert(size() != 0);
	//_finish--;
	erase(end() - 1);
}

至于查和改,就是一个遍历而已,这边不再进行实现

capacity()和size()

返回空间大小和实际使用空间的大小

cpp 复制代码
size_t capacity()
{
	size_t cp = _endofstorage - _start;
	return cp;
}
size_t capacity() const
{
	size_t cp = _endofstorage - _start;
	return cp;
}
size_t size()
{
	size_t sz = _finish - _start;
	return sz;
}
size_t size() const
{
	size_t sz = _finish - _start;
	return sz;
}

reserve和resize

这边要记得,同样不能使用memmove或者memcpy这种函数,容易造成浅拷贝问题,应该要遍历一遍原空间,然后一个一个赋值给新空间,这样才能实现深拷贝

cpp 复制代码
void reserve(size_t n)
{
	if (n > capacity())
	{
		iterator tmp = new T[n];
		iterator cur1 = _start;
		iterator cur2 = tmp;
		size_t cp = _finish - _start;
		while (cur1 != _finish)
		{
			*cur2 = *cur1;
			cur2++;
			cur1++;
		}
		delete[] _start;
		_start = tmp;
		_finish = _start + cp;
		_endofstorage = _start + n;
	}
}
void resize(size_t n, const T& val = T())
{
	if (n <= size())
	{
		_finish = _start + n;
	}
	else
	{
		if (n > capacity())
		{
			reserve(n);
		}
		iterator tmp = _finish;
		while (tmp != _start + n)
		{
			*tmp = val;
			tmp++;
		}
	}
}

assess

这边要重载两种,不然const对象无法使用[ ]

cpp 复制代码
vector<T>& operator=(const vector<T> v)
{
	swap(v);
}
T& operator[](size_t index)
{
	return *(_start + index);
}

vector的迭代器

这边vector的迭代器跟string一样,用的是原生指针

cpp 复制代码
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
	return _start;
}
iterator end()
{
	return _finish;
}
const_iterator cbegin() const
{
	return _start;
}
const_iterator cend() const
{
	return _finish;
}

vector实现的完整代码

cpp 复制代码
#include<string>
#include<assert.h>
using namespace std;
namespace vientiane
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator cbegin() const
		{
			return _start;
		}
		const_iterator cend() const
		{
			return _finish;
		}
		//construct and destroy
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}
		//用n个值构造
		vector(size_t n, const T& val = T())
		{
			_start = new T[n];
			_finish = _start + n;
			_endofstorage = _start + n;
			iterator cur = _start;
			while (cur != _endofstorage)
			{
				*cur = val;
				cur++;
			}
		}
		vector(int n, const T& val = T())
		{
			_start = new T[n];
			_finish = _start + n;
			_endofstorage = _start + n;
			iterator cur = _start;
			while (cur != _endofstorage)
			{
				*cur = val;
				cur++;
			}
		}
		template<class InputIterator>
		vector(InputIterator begin, InputIterator end)
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			while (begin != end)
			{
				push_back(*begin);
				begin++;
			}
		}
		vector(const vector<T>& v)
		{
			_start = new T[v.capacity()];
			_finish = _start + v.size();
			_endofstorage = _start + v.capacity();
			iterator cur = _start;
			for (size_t i = 0; i < v.size(); i++)
			{
				*cur = v[i];
				cur++;
			}
		}
		void swap(vector<T> v)
		{
			_start = v._start;
			_finish = v._finish;
			_endofstorage = v._endofstorage;
		}
		vector<T>& operator=(const vector<T> v)
		{
			swap(v);
		}
		T& operator[](size_t index)
		{
			return *(_start + index);
		}
		const T& operator[](size_t index) const
		{
			return *(_start + index);
		}
		~vector()
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}
		size_t capacity()
		{
			size_t cp = _endofstorage - _start;
			return cp;
		}
		size_t capacity() const
		{
			size_t cp = _endofstorage - _start;
			return cp;
		}
		size_t size()
		{
			size_t sz = _finish - _start;
			return sz;
		}
		size_t size() const
		{
			size_t sz = _finish - _start;
			return sz;
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				iterator tmp = new T[n];
				iterator cur1 = _start;
				iterator cur2 = tmp;
				size_t cp = _finish - _start;
				while (cur1 != _finish)
				{
					*cur2 = *cur1;
					cur2++;
					cur1++;
				}
				delete[] _start;
				_start = tmp;
				_finish = _start + cp;
				_endofstorage = _start + n;
			}
		}
		void resize(size_t n, const T& val = T())
		{
			if (n <= size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				iterator tmp = _finish;
				while (tmp != _start + n)
				{
					*tmp = val;
					tmp++;
				}
			}
		}
	//modify
		void push_back(const T& val)
		{
			/*if (size() == capacity())
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = val;
			_finish++;*/
			insert(end(), val);
		}
		void pop_back()
		{
			assert(size() != 0);
			//_finish--;
			erase(end() - 1);
		}
		//返回插入数据的位置
		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start && pos <= _finish);
			if (size() == capacity())
			{
				size_t n = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + n;
			}
			iterator cur = _finish;
			while (cur != pos)
			{
				*cur = *(cur - 1);
				cur--;
			}
			*pos = val;
			_finish++;
			return pos;
		}
		//返回删除数据位置的下一个位置
		iterator erase(iterator pos)
		{
			assert(size() != 0);
			assert(pos >= _start && pos <= _finish);
			iterator cur = pos + 1;
			while (cur != _finish)
			{
				*(cur - 1) = *cur;
				cur++;
			}
			_finish--;
			return pos;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}
相关推荐
捕鲸叉2 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer2 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq2 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
青花瓷4 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
幺零九零零5 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
捕鲸叉5 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
Dola_Pan6 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
yanlou2336 小时前
KMP算法,next数组详解(c++)
开发语言·c++·kmp算法
小林熬夜学编程6 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
阿洵Rain7 小时前
【C++】哈希
数据结构·c++·算法·list·哈希算法