简单模拟vector的特性,同时浅谈erase操作后,迭代器失效的原因

        阅读《STL源码剖析》有感,根据vector特性,基于数组,手动模拟一个vector。首先定义几个typedef,方便指针的定义。

template<typename T>
class Vector
{
public:
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type* iterator;
	typedef value_type& reference;
public:
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type* iterator;
	typedef value_type& reference;

protected:
	iterator start; //表示目前使用空间的头
	iterator finish; //目前使用空间的尾
	iterator end_of_storage; //目前可用空间的尾
};

        定义一个迭代器类型iterator(指针),之后定义三个迭代器,start(表示目前使用空间的头)、finish(目前使用空间的尾)、end_of_storage(目前可用空间的尾)。之后通过对指针的操作,以及数组的扩容模拟vector。全部源码如下:

//Vector.h
#pragma once
#include <memory>
template<typename T>
class Vector
{
public:
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type* iterator;
	typedef value_type& reference;
public:
	Vector(int lenght = 0) :start(nullptr), finish(nullptr), end_of_storage(nullptr)
	{
		start = new value_type[lenght];
		finish = start;
		end_of_storage = &start[lenght];
	}
	~Vector()
	{
		delete[]start;
		start = nullptr;
	}
public:
	iterator begin()
	{
		return start;
	}

	iterator end()
	{
		return finish;
	}

	int size()
	{
		return static_cast<int>(end() - begin());
	}

	void push_back(const T & x)
	{
		if (finish != end_of_storage)
		{
			*finish = x;
			finish++;
		}
		else
		{
			int num = size();
			if (num <1)
			{
				iterator newstart = new value_type[2];
				delete []start;
				start = nullptr;
				start = newstart;
				finish = start;
				end_of_storage = &start[2];
				*finish = x;
				finish++;
			}
			else
			{
				iterator newstart = new value_type[num * 2];
				memcpy(newstart,start, sizeof(value_type) * num);
				delete[]start;
				start = nullptr;
				start = newstart;
				finish = start+num;
				*finish = x;
				end_of_storage = &start[num * 2];
				finish++;
			}
		}
	}
	
	void pop_back()
	{
		if (finish != start)
		{
			finish--;
		}
		
	}

	reference operator[](int n)
	{
		return *(begin() + n);
	}

	iterator erase( const iterator  position)
	{
		if (position + 1 != end())
		{
			int num = static_cast<int>(finish - (position+1));
			memmove(position, position+1, sizeof(value_type)*(num));
		}
		--finish;
		return position;
	}

   	iterator erase(iterator  first, iterator  last)//[first,last)
	{
		if (first + 1 != end())
		{
			int num = static_cast<int>(finish - last);
			memmove(first, last, sizeof(value_type)*(num));
			finish = finish - (last - first);
		}
		return first;
	}

	void clear()
	{
		erase(begin(),end());
	}

	int capacity()
	{
		return static_cast<int>(end_of_storage - begin());
	}

protected:
	iterator start; //表示目前使用空间的头
	iterator finish; //目前使用空间的尾
	iterator end_of_storage; //目前可用空间的尾
private:

};

        在vector的构造函数中,通过传入的参数,开辟空间大小。默认参数为零。在构造函数中,start和finish指针,指向开辟空间的首地址,end_of_storage指向最后的地址。begin()和end() ,分贝返回start指针和finish指针。push_back()函数,在传入值时,所占空间已满,开辟之前二倍的空间,并将之前的值拷贝到刚开辟的空间中,同时,释放原来的空间,finish指针向后移动。pop_back()函数实现较为简单,将finish指针向前移动。其他的各个操作,都是对指针操作得到结果。测试代码:

#include <iostream>
#include "Vector.h"

int main()
{
	Vector<int> vector;

	vector.push_back(1);
	vector.push_back(2);
	vector.push_back(3);
	vector.push_back(4);
	vector.push_back(5);

	int temp = vector.size();
	int temp1 = vector.capacity();
	printf("输入的元素:\n");
	for (Vector<int>::iterator item = vector.begin();item !=vector.end();item++)
	{
		printf("%d\n",*item);
	}
	printf("所存数据 = %d,空间所占大小 = %d\n", temp, temp1);
	vector.pop_back();
	vector.pop_back();
	vector.pop_back();
	vector.pop_back();
	int temp3 = vector.size();
	int temp4 = vector.capacity();
	printf("出栈后剩余的元素:\n");
	for (Vector<int>::iterator item = vector.begin(); item != vector.end(); item++)
	{
		printf("%d\n", *item);
	}
	printf("所存数据 = %d,空间所占大小 = %d\n", temp3, temp4);
	return 0;
}

输出结果:

        最后聊聊erase() 成员函数,此函数,在STL vector中,通过vs2015测试时,通过迭代器删除一个元素时,不管它 “ * ”取值,还是++操作,都会崩溃。

for (std::vector<int>::iterator item =hvector.begin();item!=hvector.end();item++)
	{
		printf("%d\n", *item);
		if (*item == 3)
		{
			hvector.erase(item);
			 int i = *item;//崩溃
		}

        //item++时崩溃
	}

        网上更多的说法,item被删除掉,成了野指针。但是当我看见《STL源码剖析》时,erase()的实现如下:

在这里只是进行了位置的偏移,同时释放掉最后一个元素。(至此,我在模拟时,也进行了位置的偏移), 从这里可以看到,并不是因为野指针的缘故。

参看这篇博客C++ vector容器erase操作后iterate失效真相 ,这里作者提到vs中erase()参数是const_iterator,所以++时会报错。我没理解到作者的观点。各位读者知道后,请互相交流学习一下,十分感激。网上众说纷纭,自己也没找到很有说服力的答案。至此,技术有限,这里留下一个疑点。我怀疑可能是,内存发生了改动,导致迭代器失效报错。

更新追加内容:

再次对erase操作后,迭代器失效进行了探索,有了新的发现:

vs2015,vector删除的源码:

 从这里可以看出,大体的逻辑和上述删除内容相似。最后 通过调用 --this->_Mylast() 来做到finish减一,最后通过构建 _Make_iter一个新的迭代器返回。但是在_Destroy的时候其实确实涉及不到,内存重置的问题。真正涉及迭代器失效的原因是_Orphan_range();源码如下:

 这里首先会调用_Getpfirst() 获取当前要删除的迭代器(函数的返回值在,迭代器移动的时候随之移动),(*_Pnext)->_Clrcont() 是对迭代器成员_Mycont赋空的操作。

那么此时对于*操作 报错原因查看 *操作符源码 就可知道,会抛出异常:

 同理,查看++操作源码,就可知道,报错原因。

综上所述,erase()删除元素,其实并未释放空间。测试验证:

 通过调用capacity()获取配置空间大小,删除一个元素后,发现其实它的空间并未释放掉。所以这里进一步验证上面的逻辑。

如果读者不想复制粘贴,可下载:

源码:模拟vector的简易实现-C++文档类资源-CSDN下载  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值