c++修炼之路之vector模拟实现

目录

前言:

一:在STL的开源代码中的vector的实现

二:模拟实现

1.数据成员+size()+capacity()

2.resize+reserve

 3.构造函数+析构函数+赋值重载

4.迭代器+[] 

5.push_back+insert+erase+迭代器失效问题 

三:测试用例和全部代码

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧  

前言:

在了解标准库中的vector之后,对于模拟实现vecor的各接口函数常常是面试时的重难点,在模拟实现vector的过程中主要有迭代器失效的问题,在模拟实现的vectro中是使用三指针来模拟实现vector的,接下来我们就开始介绍vector的模拟实现

一:在STL的开源代码中的vector的实现

对于vector的模拟实现和string的完全不同,vector采用的是三指针来模拟实现

使用的是start,finish,endofstorage三个指针 ,在看源代码时如果不懂这三个指针的指向位置的话,则可以先看size和capacity的实现来猜测,则

finish-start=size 有效数据的大小

endofstorage-start=capacity 容量

二:模拟实现

1.数据成员+size()+capacity()

typedef T*  iterator;

private:
	iterator _start=nullptr;
	iterator _finish=nullptr;
	iterator _endofstorage=nullptr;

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

2.resize+reserve

对于在reserve的操作中会出现一些问题,首先是如果T是自定义类型的话,不能使用浅拷贝,得使用深拷贝,在操作过程中注意_finish的大小,如果不注意的话,_finish是不改变的,导致没有扩容,访问数据的话会直接越界访问报错

void reserve(size_t n)//开空间
{
	if (n > capacity())
	{
		T* tmp = new T[n];//开空间
		size_t sz = size();

		if (_start)
		{
			//问题1:
			// memcpy是按字节来拷贝的,完成的是浅拷贝,如果T是string的话
			//浅拷贝会直接出错的,应该是实现为深拷贝
			//memcpy(tmp, _start, sizeof(T) * size());//拷贝数据
			for (int i = 0; i < sz; i++)
			{
				tmp[i] = _start[i];
			}
			delete[]_start;//释放旧空间
		}
		//改变指向
		_start = tmp;
		//_finish = _start + size();
		//问题2:这样写的话,size()=_finish-_start   =>  _finish=_finish;
		// 还是原先的_finish没有改变(扩容)在访问数据时就会出错
		_finish = _start + sz;
		_endofstorage = _start + n;
	}
}
void resize(size_t n, const T& val = T())//匿名对象具有常行,加const修饰
{
	if (n <= size())//删除数据
	{
		_finish = _start + n;
	}
	else
	{
		reserve(n);
		
		while (_finish<_start+n)
		{
			*_finish = val;
			++_finish;
		}
	}
}

 3.构造函数+析构函数+赋值重载

注意尤其是拷贝构造,如果T是自定义类型的话,我们不写编译器自动生成的完成的是浅拷贝,浅拷贝就会发生问题的,所以得自己实现深拷贝

vector()//无参构造
{}

//使用一段迭代器区间来构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

vector(size_t n, const T&val=T())//n个val构造   //缺省值给匿名对象
{
	reserve(n);//开空间
	for (int i = 0; i < n; i++)
	{
		push_back(val);
	}
}

//处理vector<int> v1(10,0)这种的,如果不重载的话,会自动匹配到迭代器区间构造
vector(int n, const T& val = T())//n个val构造   //缺省值给匿名对象
{
	reserve(n);//开空间
	for (int i = 0; i < n; i++)
	{
		push_back(val);
	}
}

//拷贝构造 vector<int> v2(v1)
vector(const vector<T>& v)
{
	reserve(v.capacity());//开空间
	//最好进行深拷贝
	for (auto& c : v)
	{
		push_back(c);
	}
}

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}
//赋值重载 v1=v2;
vector<int>& operator=(vector<int> v)
{
	swap(v);
	return *this;
}

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

4.迭代器+[] 

迭代器有着普通迭代器和const成员使用的const迭代器,有了迭代器就支持范围for来遍历数据

对于下标+[]一样,也有const对象遍历时使用的

typedef T*  iterator;
typedef const T* const_iterator;

iterator begin()
{
	return _start;
}
iterator end()
{
	return _finish;
}

const_iterator begin()const
{
	return _start;
}
const_iterator end()const
{
	return _finish;
}
T &operator[](size_t pos)
{
	assert(pos < size());
	return *(_start + pos);
}

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

5.push_back+insert+erase+迭代器失效问题 

迭代器失效就是迭代器底层对应指针指向的空间被销毁了,而使用一块已经被释放的空间,程序会崩溃,引起迭代器失效的这些操作主要是与扩容有关的操作

对于insert插入数据要扩容时,由于之前的pos在扩容之前的数组中。在扩容后原数组被释放,pos还在原数组中,就会变为野指针,在扩容后的数组中没有pos指针,在insert操作时就报错,解决办法就是重新赋值

对于erase也是如此会有迭代器失效的问题, 

 

这里使用的是传值传参,形参的改变不影响实参,底层空间是不改变的,如果没有返回 值的话,pos就会失效的,下次使用vs就会直接报错的,解决办法就是传值返回

 

void push_back(const T& val)
{
	if (_finish == _endofstorage)
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}
	*_finish = val;
	++_finish;

	//insert(end(), val);复用
}
void insert(iterator pos,const T& val)//在pos位置插入val
{
	assert(pos >= _start);
	assert(pos <= _finish);
	if (_finish == _endofstorage)
	{
		//问题3:需要注意在开空间后pos位置还在原数组,此时delete原数组后,pos变成野指针
		//解决办法:先保存下来与_start的距离,在开空间之后在_start+距离即是pos的位置
		size_t len = pos - _start;
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		pos = _start + len;
	}
	//挪动数据
	iterator end = _finish;
	while (end > pos)
	{
		*end = *(end - 1);
		--end;
	}
	*pos = val;
	++_finish;
}
iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);
	// vs2019进行强制检查,erase以后认为it失效了,不能访问,访问就报错
	//解决办法:使用返回值
	iterator end = pos + 1;
	while (end < _finish)
	{
		*(end - 1) = *end;
		++end;
	}
	--_finish;
	return pos;
}

三:测试用例和全部代码

https://gitee.com/lin-ciyu/cplusplus/tree/master/my_vector/my_vector

对于vector的模拟实现还是有好多细节的,由于是迭代器失效问题和深浅拷贝问题等都须注意

 

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值