[C++]vector

文章详细讨论了C++标准库中的vector容器在使用中可能出现的问题,包括[]和at()方法的越界访问处理,以及迭代器失效的情况。在删除元素时,直接++it可能会导致错过某些元素,正确的做法是使用erase返回的迭代器。此外,文章还提供了一个简单的vector模拟实现,涵盖了reserve、resize、insert等关键操作,强调了内存管理和迭代器管理的重要性。
摘要由CSDN通过智能技术生成


一、vector的点


1. [] 和 at() 的越界访问


	vector<int> v1{ 1,2,3,4 };

	//v1[4] = 5; //越界直接结束,assert判断

	try
	{
		v1.at(4) = 5; //抛异常
	}
	catch (...)
	{
		cout << "beyond" << endl;  //输出beyond
	}

2.迭代器失效问题


简单来说就是在迭代器创建后
使用了reverse,resize,insert,push_back等导致的增容
导致迭代器仍然指向已经被释放的旧空间

一个应对迭代器失效的过程

    //删除容器中所有的偶数
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it); 
		}
		//然后该怎么++it?
		
		++it;  //此时会报错,为什么?
		        //erase删除一个数据后,其他数据向前移动,移动后其实it已经指向我们想要遍历的下一个元素
		        //此时再++it就会导致错过部分数据

		else
		{
			++it;  //那么这样处理可以么?
			       //Linux环境下这样可以达到目的,但是vs编译环境自动检查且较为严格,仍然报错
		}
	}
	
	
    //正确做法
	vector<int>::iterator it2 = v.begin();
	while (it2 != v.end())
	{
		if (*it2 % 2 == 0)
			it2 = v.erase(it2); //正确做法 erase会返回删除的it的下一个位置的迭代器
		else
			++it2;  
	}

二、vector的模拟实现


有以下要点:

reserve中浅拷贝memmove问题
新型拷贝构造和重载赋值符写法
insert时的迭代器失效

namespace 9TSe
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator; 
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{}

		//v2(v1)
		//vector(const vector<T>& v)
		//{
		//	_start = new T[v.capacity()];     //开辟新空间
		//	_finish = _start;
		//	_endofstorage = _start + capacity();

		//	for (size_t i = 0; i < v.size(); ++i)
		//	{
		//		*_finish = v[i];
		//		++_finish;
		//	}
		//}

		//拷贝构造的另一种方法
		vector(const vector<T>& v)
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			for (auto& e : v)
			{
				reserve(v.capacity());
				push_back(e);
			}
		}

		//vector<T>& operator=(const vector<T>& v)
		//{
		//	if (*this != &v) //防止自己赋值给自己
		//	{
		//		delete[] _start; //s1 = s2 s1有自己的空间,先释放
		//		_start = new T[v.capacity()];

		//		memcpy(_start, v._start, sizeof(T) * v.size());
		//	}
		//	return *this;
		//}

		//重载赋值运算符简便写法
		void swap(vector<T>& v)
		{
			::swap(_start, v._start); 
			::swap(_finish, v._finish);
			::swap(_endofstorage, v._endofstorage);
		}

		vector<T>& operator=(vector<T> v)
		{
			if (*this != &v)
				swap(v);		//这里不用库内自带的swap是因为,其会生成三个深拷贝,有很大的代价
			return *this;
		}

		~vector()
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}

		void reserve(size_t n)
		{
			if (n > capacity()) //增大容量时
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start) //这里的用意是防止_start为空指针时扩容,会导致memcpy出错(memcpy内不能存在空指针)
				{
					//memcpy(tmp, _start, sizeof(T) * size()); //这个memcpy是浅拷贝,当T为string时,会导致指针指向同一位置,释放两次同一空间
					for (size_t i = 0; i < sz; ++i)
					{
						tmp[i] = _start[i];
					}
					delete[] _start; 
				}
				_start = tmp;
				_finish = tmp + sz; //如果这里赋予 tmp + size() 会出错 
								    //sz的计算方式是_finish-_start
									//_start已经指向新空间了,finish还指向旧空间
				_endofstorage = tmp + n;//这里如果赋予tmp + capacity() 也会出错,与上同理
			}
			
		}

		void resize(size_t n, const T& v = T()) //这里的T()为T类型的默认缺省值,有int() double() 等都有
									   //int() double()等内置类型,只是专门创建了此形式的东西
									   //string ,vector 是调用构造函数
		{
			if (n < size()) //缩容
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}

				while (_finish < _start + n)
				{
					*_finish = v;
					++_finish;
				}
			}
		}

		void push_back(const T& x)
		{
			if (_finish == _endofstorage) //扩容
			{
				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			++_finish;
		}


		void insert(iterator pos, const T& v) //这里会有迭代器失效问题
		{
			assert(pos <= _finish);
			if (_finish == _endofstorage)//pos会扩容后失效
			{
				size_t n = pos - _start; //因此要记录之间的距离,以明确新pos指针指向的位置

				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
				reserve(newcapacity);

				pos = _start + n; //设置新pos
			}

			iterator end = _finish - 1;
			while (end >= pos)   //移动数据
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = v;
			++_finish;
		}

		iterator erase(iterator pos)
		{
			assert(pos < _finish&& _start != _finish);
			iterator it = pos;
			while (it < _finish)
			{
				*it = *(it + 1);
				++it;
			}
			_finish--;
			return pos;
		}


		size_t size()const
		{
			return _finish - _start;
		}

		size_t capacity()const
		{
			return _endofstorage - _start;
		}

		iterator end()
		{
			return _finish;
		}

		iterator begin()
		{
			return _start;
		}

		const_iterator end()const
		{
			return _finish;
		}

		const_iterator begin()const
		{
			return _start;
		}

		T& operator[](size_t i)
		{
			assert(i < size());
			return *(_start + i);
		}

		const T& operator[](size_t i)const
		{
			assert(i < size());
			return *(_start + i);
		}

		void pop_back()
		{
			assert(_start < _finish);
			_finish--;
		}

	private:
		iterator _start;
		iterator _finish; //这个指针指向的是最后一个数据的下一个数据的位置
		iterator _endofstorage;
	};
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值