当前位置:网站首页>vector的使用和模拟实现:
vector的使用和模拟实现:
2022-08-02 03:32:00 【RNGWGzZs】

--------------血雨腥风,是对行远人的痛快的激励
目录
(1)Vector是什么?
vector是表示可变大小数组的序列容器。和数据结构的 顺序表尤为相似。
因为模板的介入,vector能存储的类型多样,且方便。

(2)Vector的功能使用:
同上篇string(串)的功能 差不多,vector的基本功能颇为丰富:
①构造类:
(constructor)构造函数声明 | 函数说明 |
vector() | 无参构造 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& x); | 拷贝构造 |
vector (InputIterator fifirst, InputIterator last); | 使用迭代器进行初始化构造 |

② 容量空间:
容量空间 | 函数说明 |
| size | 有多少个有效数据 |
| capacity | vector的总容量 |
| empty | 有没有数据 |
| resize | 扩容,并初始化 |
| reserve | 扩容 |
resize:

reserve:

③modify:
vector增删查改 | 函数说明 |
| push_back | 尾插 |
pop_back | 尾删 |
find | 查找 |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换两个vector的数据空间 |
operator[] | 像数组一样访问 |
push_back\pop_back:

find\ insert;erase:


注:erase 和 insert的 参数是迭代器>
insert:

erase:

(2)Vector的模拟实现:

namespace dy
{
//vector 是可以存储多种类型的顺序表
template<class T>
class vector
{
//vector的迭代器是个原生指针
typedef T* iterator;
typedef const T* const_iterator;
public:
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}①构造:
//无参构造
vector()
: _start(nullptr),
_finish(nullptr),
_endofstorage(nullptr)
{}
//迭代器构造
template<class templateiterator>
vector(templateiterator first, templateiterator last)
:_start(nullptr),
_finish(nullptr),
_endofstorage(nullptr)
{
while (first != last)
{
//复用尾插
push_back(*first); //push_back 之后会实现
first++;
}
}
//默认值初始化
vector(size_t n, const T& val = T()) //因为缺省值不知道 具体给什么,所以就用匿名对象
:_start(nullptr),
_finish(nullptr),
_endofstorage(nullptr)
{
reserve(n); //开辟n个空间
while (n--)
{
push_back(val);
}
}
//析构
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}capactiy、size、operator[] :
size_t capacity() const
{
return _endofstorage-_start; //起始位置减去 容量位置
}
size_t size() const //最好+上
{
return _finish-_start; //起始位置减去 最后数字的位置
}
T& operator[](size_t pos)
{
assert(_start+pos < _finish)
return _start[pos];
}拷贝构造:

传统写法:
//传统写法
//拷贝构造
//s2(s1)
vector(const T& v)
:_start(new T[v.capacity()]), //开出同样大的空间
_finish(nullptr),
_endofstorage(nullptr)
{
//将s1 的数据拷贝给s2
memcpy(_start, v._start, sizeof(T) * v.size());
//更改_finish _endofstorage
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
//赋值运算符
//s1=s2
vector<T>& operator=(const vector<T>& v) //operator(&s1,s2);
{
if (this != &v) //自己和自己不赋值
{
delete[] _start; //先释放自己的空间
_start= new T[v.capacity()];
//拷贝数值
memcpy(_start, v._start, sizeof(T) * v.size());
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
return *this;
}上面是传统写法,代码较为冗杂,
现代写法:
//现代写法:
void swap(const vector<T>& tmp)
{
//局部和全局分离
::swap(_start, tmp._start);
::swap(_finish,tmp._finish);
::swap(_endofstorage, tmp._endofstorage);
}
//赋值
vector<T>& operator=(const vector<T>& v)
{
if (*this != &v)
{
/*
swap(_start, tmp._start);
swap(_finish,tmp._finish);
swap(_endofstorage, tmp._endofstorage);
*/
//this->swap(tmp)
swap(tmp) //当然这三个swap实现的功能 一样 可以抽象出来
}
return *this;
}现代写法不仅兼顾了理解,且代码量少。
②modify:
push_back\reserve:
尾插框架很简单,就分是否需要扩容:
reserve的一大容易被忽略的点在这里。我们先看看测试:
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end()const
{
return _finish;
}
运行起来后程序直接崩了:

那么问题出在哪里呢?

所以,就要先保存差值:

void reserve(size_t n)
{
if (n > capacity()) //解耦
{
//新开 n 个空间
T* tmp = new T[n];
//避免找不到真正的size
size_t sz = size();
//可能_start为空
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);
delete[] _start;
}
//更新
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n; //开辟了n个空间
}
}
void push_back(const T& val)
{
//需要增容
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; //2倍扩容
reserve(newcapacity);
}
*_finish = val; //直接怼_finish 解引用找到最后一数的下一个位置
_finish++;
} 
resize/pop_back:
尾删较为简单也就多赘述
bool empty()
{
return _finish = _start;
}
void pop_back()
{
assert(empty())
--finish;
}
void resize(size_t n, const T& val=T()) //给定缺省值
{
//1.缩减
if (n < size())
{
_finish = _start + n;
}
else
{
//统一是n>size()
if (n > capacity()) //如果需要的空间大 那么再去申请空间
{
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
③补充:
insert\erase:
在pos之前的位置,插入(insert)数值:

我们可以借助,库里面find函数,查找pos位置:
void Insert(iterator pos, const T& val)
{
assert(pos < _finish);
//扩容
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; //2倍扩容
reserve(newcapacity);
}
//挪动pos之后的数据
//从后往前
iterator end = _finish - 1;
while (end != pos)
{
*(end + 1) = *end;
--end;
}
//pos位置处 放val
*pos = val;
_finish++;
}
从结果上来看,我们代码与我们预期相同。然而真的是这吗?

此时我们换种情景:程序直接崩溃,而原因又在哪里呢?!
迭代器失效:
1.迭代器野指针:
我们不难猜到,问题最有可能出现的,就是在增容的部分,而恰恰第四次插入,就是需要增容~


所以,我们就需要对pos ,在扩容的时候,进行修正。

我们让gap记录 start 与 pos之间的相对差距。

void Insert(iterator pos, const T& val)
{
assert(pos < _finish);
//扩容
if (_finish == _endofstorage)
{
size_t gap = pos - _start;
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; //2倍扩容
reserve(newcapacity);
pos = _start + gap;
}
//挪动pos之后的数据
//从后往前
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
//pos位置处 放val
*pos = val;
_finish++;
}2.迭代器代表的位置发生改变:

插入数据不可避免,会改变原先pos位置所指向的值。所以在用这种迭代器时,需要加点心。
erase:

我们根据库里的来模拟,erase除了删除pos位置的数值,还要返回pos位置,下一个的地址。
iterator erase(iterator pos)
{
//pos后的数 往前移动
iterator begin = pos + 1;
while (begin <= _finish)
{
*(begin - 1) = *begin;
begin++;
}
_finish--;
return pos;
}另:删除顺序表中的偶数
可能多数,认为删除偶数这样的逻辑是行得通的,比较也有结果。

只要找到偶数,就删掉。

换种数据就不行了。

所以,每次it进行判断的时候,每删除一个,还需要进行判断。

while (it != v1.end())
{
if (*it % 2 == 0)
{
it=v1.erase(it); //利用erase的返回值
}
else
{
it++;
}
}(3)深层次深拷贝:
大部分vector 重要的函数接口已经完成。

但,这样的vector在存储自定义类,而非内置类型是,会出现问题。
例如:
此时我们插入四个值,还算正常。

但当插入第五个数的时候。


所以问题还是在于,扩容。


但为啥,当数据足够长的时候,会出现乱字呢?!
在linux下,string不管有多长,都会存在“buf”里面,
但是在vs下,做了别的处理,

当串字符足够小,就存在_buf里面 ,越长 则存在_ptr

所以,这里用memcpy显然不合适,如果仅仅处理内置类型是足够的。
所以和memcpy有关的,就需要更改:
//拷贝构造
//s2(s1)
vector(const vector<T>& v)
:_start(nullptr), //开出同样大的空间
_finish(nullptr),
_endofstorage(nullptr)
{
reserve(v.capacity()); //进行增容
for (auto e : v)
{
push_back(e);
}
//传统写法
//_start = new T[v.capacity()];
将s1 的数据拷贝给s2
memcpy(_start, v._start, sizeof(T) * v.size());
//int sz = size();
深层次考别
//for (size_t i = 0;i < v.size();i++)!!
//{
// _start[i] = v._start[i];
//}
更改_finish _endofstorage
//_finish = _start + v.size();
//_endofstorage = _start + v.capacity();
}
void reserve(size_t n)
{
if (n > capacity()) //解耦
{
//新开 n 个空间
T* tmp = new T[n];
//避免找不到真正的size
size_t sz = size();
//可能_start为空
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0;i < sz;i++)
{
//复用 赋值运算符 这是深拷贝~
tmp[i] = _start[i];
}
delete[] _start;
}
//更新
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n; //开辟了n个空间
}
}
vector也就告一段落啦!
祝你好运~

边栏推荐
- TeamCode 产品 UI 全新升级,快来体验吧
- 倍福ET2000侦听器使用
- D类音频功放NS4110B电路设计
- 2020 - AAAI - Image Inpainting论文导读《Learning to Incorporate Structure Knowledge for Image Inpainting》
- 使用批处理脚本修改hosts文件
- 【Arduino 连接 SD 卡模块实现数据读写】
- 【Arduino 连接DHT11 湿度和温度传感器】
- 【plang 1.4.5】编写坦克(双人)游戏脚本
- 2019 - ICCV - 图像修复 Image Inpainting 论文导读《StructureFlow: Image Inpainting via Structure-aware ~~》
- [Arduino uses a rotary encoder module]
猜你喜欢

【plang 1.4.6】Plang高级编程语言(发布)

AD8307对数检波器

【Popular Science Post】UART Interface Communication Protocol

【Arduino 连接DHT11 湿度和温度传感器】

增量编译技术在Lightly中的实践

GM8775C规格书,MIPI转LVDS,MIPI转双路LVDS分享

Pylon CLI 低成本的本地环境管控工具应用实例

Industry where edge gateway strong?

AD PCB导出Gerber文件(非常详细的步骤)

【Arduino connects DHT11 humidity and temperature sensor】
随机推荐
使用Vercel托管自己的网站
GM8775C MIPI转LVDS调试资料分享
【Arduino连接时钟模块在LCD1602上显示时间】
如何用 Lightly 进行 Debug 断点调试?
引擎开发日志:集成Bullet3物理引擎
【科普贴】SD卡接口协议详解
【Arduino connects SD card module to realize data reading and writing】
字符串匹配(蛮力法+KMP)
【科普贴】SPI接口详解
USB HUB USB集线器电路设计
火焰传感器与 Arduino 连接
Lightly 支持 Markdown 文件在线编写(文中提供详细 Markdown 语法)
联阳IT66121FN提供SDI转HDMI方案分享
Chrome 里的小恐龙游戏是怎么做出来的?
开源代码交叉编译操作流程及遇到的问题解决(lightdm)
How to remotely debug PLC?
阿里云华为云对比分析
HDMI转MIPI CSI东芝转换芯片-TC358743XBG/TC358749XBG
远程调试PLC,到底如何操作?
增量编译技术在Lightly中的实践