C++——vector的使用以及模拟实现

本文详细介绍了C++ STL中的vector容器,包括构造函数、迭代器使用、容量增长策略、增删查改操作,并模拟实现了vector的部分核心功能,如构造函数、resize、insert和erase。同时讨论了迭代器失效的问题以及reserve方法的实现,强调了在处理vector时要注意的迭代器安全性和内存管理策略。
摘要由CSDN通过智能技术生成

1 vector的定义

vector学习时一定要学会查看文档:vector的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常
见的接口就可以,下面列出了哪些接口是要重点掌握的。

(constructor)构造函数声明接口说明
vector()(重点)无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector (const vector& x); (重点)拷贝构造
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

2 vector iterator 的使用

iterator的使用接口说明
begin + end(重点)获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

在这里插入图片描述

3 vector 空间增长问题

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize(重点)改变vector的size
reserve (重点)改变vector的capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。 这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
  • resize在开空间的同时还会进行初始化,影响size

4 vector 增删查改

vector增删查改接口说明
push_back(重点)尾插
pop_back (重点)尾删
find查找。(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[] (重点)像数组一样访问

5 vector模拟实现

在STL源码中,vector类维护有三个迭代器(三个类型指针)start, finish, end_of_storage,分别代表头, 尾(实际使用的), vector 存储尾部(占用的,也就是实际所开辟的空间,通常大于实际使用)。
在这里插入图片描述

private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;

构造函数

1、无参构造:

vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{}

2、构造并初始化n个val:

vector(int n, const T& val = T())
    :_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	reserve(n);
	for (int i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

vector(size_t n, const T& val = T())
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	reserve(n);
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

3、使用迭代器进行初始化构造:

template <class InputIterator>
	vector(InputIterator first, InputIterator last)
		:_start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)
	{
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

4、拷贝构造:

vector(const vector<T>& v)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
	{
		vector<T> tmp(v.begin(), v.end());
		swap(tmp);
	}

赋值运算符重载:

vector<T>& operator=(vector<T> v)
	{
		swap(v);
		return *this;
	}

析构函数:

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

resize()模拟实现

void resize(size_t n, T val = T())
{
	if (n > capacity())
	{
		reserve(n);
	}

	if (n > size())
	{
		while (_finish < _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
	else
	{
		_finish = _start + n;
	}
}

增删查改判空的函数

bool empty() const
{
	return _finish == _start;
}

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

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

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

	*_finish = x;
	++_finish;
}

void pop_back()
{
	assert(!empty());

	--_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];
}

其他成员函数以及成员变量:

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}

void clear()
{
	_finish = _start;
}
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;
		}
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;
};

6 vector 迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,但是这个迭代器它自己不知道,还是使用原来的那一块空间,但是已经被释放了,造成的后果是程序崩溃。

对于vector可能会导致其迭代器失效的操作有:
1、会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、
push_back等。

insert()模拟实现

// 迭代器失效 : 扩容引起,野指针问题
iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start);
	assert(pos < _finish);

	if (_finish == _endofstorage)
	{
		size_t len = pos - _start;
		size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newCapacity);

		// 扩容会导致pos迭代器失效,需要更新处理一下
		pos = _start + len;
	}

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

	*pos = val;
	++_finish;

	return pos;
}

erase()模拟实现

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代 器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

那么从中得出的结论就是我们在使用erase()的时候,如果原来的迭代器还想用,那么就要更新一下它。
而官方库里的erase返回值正好是被删除的迭代器的下一个位置的迭代器。
那么更新的时候就把erase的返回值赋给这个迭代器即可。
在这里插入图片描述

iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);


	iterator begin = pos+1;
	while (begin < _finish)
	{
		*(begin-1) = *(begin);
		++begin;
	}

	--_finish;

	return pos;
}

7 reserve()模拟实现以及两次深拷贝

memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中,如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,一不小心就会出错,因为memcpy的拷贝实际是浅拷贝。
假如我们要拷贝的是自定义类型里套了一个自定义类型,遇到delete[] _start;的时候,不仅会把原来的自定义类型释放掉,而且会把它每一个单位中存放的自定义类型也释放掉,这是我们不想要的。
解决方法就是复用拷贝构造或者赋值运算符重载,我们像下面的蓝框框一样,进行深拷贝,即使要delete掉原来的空间,那么它内部的自定义类型也不会受到干扰。
在这里插入图片描述

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t oldSize = size();
		T* tmp = new T[n];

		if (_start)
		{
			//memcpy(tmp, _start, sizeof(T)*oldSize);
			for (size_t i = 0; i < oldSize; ++i)
			{
				tmp[i] = _start[i];
			}

			delete[] _start;
		}

		_start = tmp;
		_finish = tmp + oldSize;
		_endofstorage = _start + n;
	}
}

这里是完整代码链接
请添加图片描述


好了今天的分享就到此为止了
最后:如果你觉得对你有用就一键三连吧,哪里有没看懂的地方或者哪里有错误可以在评论区留言欢迎批评指正,作者看到的话会第一时间回复。
end


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有效的放假者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值