前言
环境默认为C++20 MSVC。
今天复习一下向量vector吧。
注:为了方便理解而且写起来舒服点,实现的是炒鸡*n简单的版本。(既没有迭代器也没有模板,要的就是最粗犷原始的味儿!)
顺序容器(Sequence Container)
常见的顺序容器有链表(Singly-Linked List/Doubly-Linked List)、向量(vector)、双端队列(double-ended queue)等。顺序容器,顾名思义,代表存储的方式都是顺序存储的。都属于线性结构:即拥有一一对应的映射关系。提一嘴非线性结构:拥有一对多(如树Tree)或者多对多的关系(如图Graph)
Vector特点
上次说链表的时候忘记说其实链表本身也是一个顺序容器了,而链表的最大特点就是其存储空间是非连续的(虽然用指向自己结构的指针使其看上去像是连续的了,但是实际上分配的内存空间是断开的)。今天要说的向量也是顺序容器,但是和链表不太一样的是:向量vector分配的空间是连续的!
诶那就奇了怪了:这和数组有啥区别呀?
核心区别如下:
- 可以动态扩容
没了
其中需要定义的有:当前vector大小,分配的内存空间、以及需要存储的数据类型。
其中push_back相对来说效率较高,而在中间和前面插入效率较低。这是因为为了保证其数据存储的顺序性,如果在前面插入,则后面所有的数据元素都不得不进行移动。
灵魂画师大家看看就好。
Vector的插入
- Vector是否已经满了
- 位置是否合法
- 插入元素
- 移动元素
- 修改字段
注意STL vector的push_back效率在刚开始的时候较低(其实实际上push_back的效率慢是因为重新分配内存空间的速度慢),所以如果你要存的数据类型较大,则最好先分配好大概的空间,来避免过多的资源消耗。测试放在最后。
Vector的删除
- 判断是否为空
- 判断位置是否合法
- 删除元素
- 移动元素
- 修改字段’
Vector扩容
Vector的扩容本质上是3步:
- 申请一块新的内存空间
- 将旧的内存空间的数据拷贝到新的内存空间
- 将旧的内存空间释放
在这份代码中,Vector的扩容操作每次增加为原来的两倍,如果没有额外内存空间,那么不断将其除以2,试图申请更小的内存空间。如果最后申请到了内存空间,返回1,否则返回0。这里用到了realloc函数。
注意由于Vector可能会存储大量数据,因此扩容操作在申请新内存时可能会失败(内存耗尽)。new底层由malloc实现,而malloc在申请内存失败时会返回空指针nullptr,此时可以判断申请的内存空间是否是空指针来确定是否能够移动数据到新空间。
Vector简单实现代码
这里就实现最简单的:存储int类型的Vector吧!一通百通。
#include<iostream>
#include<format>
#include<cstring>
#include<vector>
#include<cstdio>
#include<queue>
#include<ctime>
using namespace std;
class Vector {
private:
int* mdata;
int msize, mcapacity;
private:
int expand() {
int extra_size = mcapacity;
int* p = nullptr;
while (extra_size) {
p = (int*)realloc(mdata, sizeof(int) * (mcapacity + extra_size));
if (p) break