C++中vector的操作和简单实现

vector本质上就是使用动态分配数组来储存元素。

如果使用数组,当新元素插入时,这个数组需要被重新分配大小,为了增加存储空间。需要开辟新空间,然后将数据转移到新数组空间中。vector和数组空间分配策略不一样,vector会分配额外空间,分配空间比实际需要存储空间更大。

1.vector的构造函数

cpp 复制代码
class vector
{
 public:
    vector()//默认构造不动
		:_start(nullptr)
		, _finish(nullptr)
	{}

	vector(vector<T>& v)//拷贝构造
	{
		reserve(v.capacity());//先扩容
		for (const auto& e : v)
		{
			push_back(e);
		}
	}

	vector(iterator first, iterator last)//迭代构造
	{
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

	vector(size_t n, const T& x = T())
	{
		resize(n, x);
	}

};	

2.iterator使用

  • begin/end 获取第一个数据位置的iterator/获取最后一个数据的下一个位置的iterator
  • 获取最后一个数据元素的reverse_iterator,获取第一个数据前一个位置的reverse_iterator
cpp 复制代码
class vector
{
    public:
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

    private:
		iterator _start;//指向第一个元素
		iterator _finish;//指向最后一个元素的后一个
		iterator _end_storage;
};

3.vector空间增长机制

  • capacity在vs下是按照1.5被扩容的,g++下是按2被扩容的。(vs是pj版本的STL,g++是SGI版本STL)
  • reverse只负责开辟空间,如果提前知道使用多少空间,就可以避免vector增容的代价缺陷问题。

reserve(size_type n)

只分配内存,不创建元素

cpp

复制代码
std::vector<int> vec;
vec.reserve(100);  // 只分配内存,size() 不变
  • 作用:预先分配至少能容纳 n 个元素的内存空间

  • size():保持不变(仍为 0)

  • capacity():增加(≥ n)

  • 元素:不创建新元素,不改变现有元素

  • 性能:避免多次重新分配,提高 push_back 效率

  • 场景:知道大致元素数量时,避免重复分配

cpp 复制代码
void reserve(size_t n)
{
	if (capacity() < n)//需要扩容
	{
		size_t oldsize = size();
		//开空间
		T* tmp = new T[n];
		if (_start)
		{
			memcpy(tmp, _start, oldsize * sizeof(T));//拷贝
			delete [] _start;
		}


		_start = tmp;
		_finish = _start + oldsize;
		_end_storage = _start + n;
	}
}

resize(size_type n)

改变容器大小,创建/销毁元素

cpp

复制代码
std::vector<int> vec;
vec.resize(100);  // 创建100个元素,size变为100
  • 作用:改变 vector 的大小,使其包含 n 个元素

  • size():变为 n

  • capacity():可能增加(如果 n > capacity)

  • 元素

    • 如果 n > 当前 size:添加新元素(默认构造或指定值)

    • 如果 n < 当前 size:删除尾部元素

  • 场景:需要立即使用索引访问元素时

cpp 复制代码
	//如果 n 大于当前容器大小,则通过在末尾插入足够多的元素来扩展内容,达到 n 的大小。
	void resize(size_t n, const T& x = T())//默认构造
	{
		size_t old = size();
		if (n > old)
		{
			reserve(n);//先扩容,再插入
			for (int i = 0; i < n - old; i++)
			{
				push_back(x);
			}
		}
		else 
		{
			_finish = _start + n;
		}
	}

4.vector增删改查

push_back尾插

如果size超过capacity,需要扩容

cpp 复制代码
		void push_back(const T& x)
		{
			if (_finish == _end_storage)//一下扩两倍
			{
				size_t newcap = capacity() > 4 ? capacity() * 2 : 4;
				reserve(newcap);
			}
			*_finish = x;
			_finish++;
		}

pop_back尾删

cpp 复制代码
		void pop_back()
		{
			assert(size() > 0);
				--_finish;
		}

insert插入

要小心扩容之后的pos不是本对象的迭代器了,需要重新计算返回

cpp 复制代码
		iterator insert(iterator pos, const T& x)//返回迭代器
		{
			//确保插入位置合法
			assert(pos >= _start && pos <= _finish);

			//如果它满了,扩容
			if (_finish == _end_storage)
			{
				size_t len = pos - _start;
				size_t newcap = size() > 4 ? 2 * size() : 4;
				reserve(newcap);
				//注意要把pos也改变了
				pos = len + _start;
			}
			

			//把pos后的元素向后移动
			iterator end = _finish-1;//_finish指向末尾元素后一个
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;

			}
			
			//插入元素
			*pos = x;
			++_finish;
			return pos;
		}

erase删除

直接把pos后的元素向前移动一位

cpp 复制代码
		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos;
			while (it <_finish-1)
			{
				*it = *(it + 1);
				++it;

			}
			--_finish;
			return pos;
		}

operator[]

cpp 复制代码
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return *(_start + pos);
		}

5.vector迭代器失效问题

迭代器实质上就是一个指针,或者是对指针做了封装,比如vetcor迭代器就是原生态指针T*。就是迭代器底层对应指针指向的空间被收回了,造成的结果就是程序奔溃。(也就是野指针)

  • 会导致存储空间变化的操作,都可能使迭代器失效,eg:resize,reserve,insert,assign,push_back(erase vs下会强制缩容也会导致迭代器失效)
  • 不应该使用erase删除后的原来迭代器
cpp 复制代码
#include <iostream>
using namespace std;
#include <vector>

int main()
{

	int a[] = { 1,2,3,4 };
	vector<int> v(a, a + sizeof(a)/sizeof(int));
	auto pos = find(v.begin(), v.end(),1);
	auto it = v.erase(pos);//pos被删除
	//cout << *pos << endl;//报错
	cout << *it << endl;
	return 0;
}
相关推荐
im_AMBER36 分钟前
Leetcode 115 分割链表 | 随机链表的复制
数据结构·学习·算法·leetcode
Coder_Boy_36 分钟前
Java开发者破局指南:跳出内卷,借AI赋能,搭建系统化知识体系
java·开发语言·人工智能·spring boot·后端·spring
数智工坊38 分钟前
【数据结构-树与二叉树】4.7 哈夫曼树
数据结构
!!!!81342 分钟前
蓝桥备赛Day1
数据结构·算法
Mr_Xuhhh42 分钟前
介绍一下ref
开发语言·c++·算法
七点半77042 分钟前
linux应用编程部分
数据结构
王老师青少年编程1 小时前
2024年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第2题)
c++·题解·真题·初赛·信奥赛·csp-s·提高组
静听山水1 小时前
Redis核心数据结构-Hash
数据结构·redis·哈希算法
nbsaas-boot1 小时前
软件开发最核心的理念:接口化与组件化
开发语言
Trouvaille ~1 小时前
【Linux】进程间关系与守护进程详解:从进程组到作业控制到守护进程实现
linux·c++·操作系统·守护进程·作业·会话·进程组