【C++】——Vector的模拟实现

P. S.:以下代码均在VS2019环境下测试,不代表所有编译器均可通过。
P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。

  

在这里插入图片描述

                                           博主主页:Yan. yan.
                                              C语言专栏
                                            数据结构专栏
                                         力扣牛客经典题目专栏
                                                     C++专栏

一、Vector的成员变量

  我们以类模板的方式来实现Vector的实现。

template<class T>

  类型名称也重新进行修改

typedef T* iterator;
typedef const T* const_iterator;

根据库的实现方式,成员函数如下:

private:
	iterator _start;
	iterator _finish;
	iterator _end_of_storage;

二、默认成员函数

1、构造函数

构造函数可分为3种:

  • 无参构造
  • 构造n个val对象
  • 根据迭代区间构造
    对于无参的构造,我们只需要初始化成员函数即可。


    无参构造
//无参构造函数
vector()
	//:_start(nullptr)
	//,_finish(nullptr)
	//,_end_of_storage(nullptr)
{}

构造n个val对象

//构造n个对象
vector(size_t n, const T& val = T())
	:_start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{
	//1.开空间
	reserve(n);
	//2.将空间填充为val对象
	for (size_t i = 0; i < capacity(); i++)
	{
		_start[i] = val;
	}
	//也可以复用resize
	//resize(n, val);
}

注意:这里在构造时必须初始化,如果不初始化,在调用reserve函数开空间时会出现访问野指针的问题。也可以调用resize函数进行拷贝构造n个对象。

使用迭代区间进行构造
同理,如果不初始化,会出现扩容后访问野指针的情况。

template<class Inputiterator>
vector(Inputiterator first, Inputiterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

2、拷贝构造函数

vector(const vector<T>& v)
//这里必须初始化,否则扩容时会访问不属于自己的空间
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
//如果私有成员给了缺省值,就不用再再次初始化了
{
reserve(v.capacity());
//memcpy(_start, v._start, sizeof(T) * v.size());
//这里使用memcpy的错误跟扩容的错误一样,不再赘述
for (size_t i = 0; i < v.size(); i++) // 如果是string类,会调用string的赋值重载,赋值重载调用string的拷贝构造,完成深拷贝
{
	_start[i] = v._start[i];
}

_finish = _start + v.size();
}

拷贝构造需要注意的几个问题:

  • 拷贝构造同构造函数一样,如果不初始化,在开空间时会出现访问野指针的情况。
  • 拷贝构造必须进行深拷贝,否则如果vector的对象是自定义类型,比如string,会出现两次释放同一块空间的问题。

3、析构函数

释放_start指向的空间,然后全部置为空即可。

~vector()
{
	if (_start)
	{
		delete[] _start;
		_start = _finish = _end_of_storage;
	}
}

三、增删查改工作

说明size()和capapcity()
在这里插入图片描述
size的大小为_finish - _start
capacity的大小为_end_of_storage - _start

1、reserve()

该函数的作用是:扩容。

void reserve (size_type n);
  • 思路:
  • 如果要扩容的大小n小于capacity,则不会缩容。
  • 否则执行扩容。
  • 先申请一块n大小的空间
  • 拷贝原空间的数据到新空间,并释放原空间
  • 将新空间的数据交给_start管理。
void reserve(size_t n)
{
	if (n > capacity())
	{
		T* tmp = new T[n]; 
		size_t sz = size();
		if (_start)
		{
			for (size_t i = 0; i < sz, i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
		}

		_start = tmp;
		_finish = _start + sz;
		_end_of_storage = _start + n;
	}
}

2、 resize()

该函数的功能是:重新改变vector容器的大小,让其到达n。新的空间默认初始化为val。

  • 思路:
  • 如果n小于size,则让_finish指向_start + n位置。
  • 如果n大于等于size,先调用reserve函数进行扩容,再将新的空间填充为val。
void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				//扩容到n,如果n<capacity(),则啥事不做,否则,扩容到n
				reserve(n);
				//将多的空间全部设置成缺省值
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

3、insert()

该函数的作用是:在pos位置插入模板对象val。

  • 思路:
  • 插入元素前先检查容量
  • 如果容量满了,则调用reserve函数执行扩容操作
  • 然后从pos位置开始将其之后的元素全部都往后挪。
  • 再在pos位置插入val。
void insert(iterator pos, const T& val)
{
assert(pos <= _finish);
//插入前先检查扩容
if (_finish == _end_of_storage)
{
	size_t oldpos = pos - _start;//记录相对位置
	size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
	reserve(newcapacity);

	pos = _start + oldpos;
}

//记录旧的size,防止出现迭代器失效问题。

//将pos之后的位置往后挪,再插入
iterator end = _finish - 1; // reserve之后,_finish也跟着更新了。
while (end >= pos)
{
	*(end + 1) = *end;
	--end;
}

*pos = val;
_finish += 1;
}

4、erase()

该函数的功能是:删除pos位置的值。

iterator erase(iterator pos)
{
	assert(pos < _finish);
	iterator end = pos;
	while (end != _finish)
	{
		*end = *(end + 1);
		++end;
	}
	--_finish;

	return pos;
}

有一个需要注意的点:当我们删除元素后,pos迭代器就会失效,因为空间已经释放,这时候不能使用pos迭代器。
解决方法:返回删除位置的下一个元素的位置,即pos位置。
然而,在只剩下一个待删除元素或者删除最后一个位置的元素时,不能再使用pos迭代器。

四、[]重载和迭代器

1、begin迭代器

返回_start地址即可。

iterator begin()
{
	return _start;
}

const_iterator begin() const
{
	return _start;
}

2、end迭代器

返回_finish地址即可。

iterator end()
{
	return _finish;
}

const_iterator end() const
{
	return _finish;
}

3、[]运算符重载

只需给定pos下标即可。
在此之前需要判断pos位置不能超过整个顺序表的size大小。

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

//不可修改的
const T& operator[]( size_t pos) const
{
	assert(pos < size());
	return *(_start + pos);
}
  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值