【STL】vector的模拟实现

目录

vector的介绍和使用

vector的介绍

vector的使用

构造函数

 迭代器

空间增长问题 

vector的增删查改等 

vector的迭代器失效问题 

vector的模拟实现

insert

reserve

push_back

push_front

resize

erase

pop_back

pop_front

代码


vector的介绍和使用

vector的介绍

1)vector是可变数组大小的序列容器

2)与数组相比,vector提供了更多的功能

3)vector会分配一些额外的空间以适应可能得增长

vector的使用

构造函数

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

代码演示:

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v1; //无参构造vector<int> v2(3, 9); //构造并初始化n个val(9 9 9)vector<int>::iterator it2 = v2.begin();while (it2 != v2.end()){cout << *it2 << " ";++it2;}cout << endl;vector<int> v3 = v2; //拷贝构造(9 9 9)vector<int>::iterator it3 = v3.begin();while (it3 != v3.end()){cout << *it3 << " ";++it3;}cout << endl;vector<int> v4(v3.begin(), v3.end()); //迭代器区间构造(9 9 9)vector<int>::iterator it4 = v4.begin();while (it4 != v4.end()){cout << *it4 << " ";++it4;}cout << endl;return 0;
}

 迭代器

函数说明
begin()获取第一个元素的迭代器
end()获取最后一个元素的下一个位置的迭代器
rbegin()获取最后一个元素的迭代器
rend()获取第一个元素前一个位置的迭代器

 代码演示:

#include <iostream>
#include <vector>
using namespace std;int main()
{int arr[] = { 1,2,3,4,5 };vector<int> v(arr, arr + 5); //5 4 3 2 1 vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";++rit;}cout << endl;return 0;
}

空间增长问题 

函数说明
size()获取数据个数
empty()判断是否为空
capacity()获取容量大小
resize()改变vector的size
reserve()改变vector的capacity

1)在vs下,capacity是按1.5倍增长,g++下是按2倍增长

2)reserve只负责开辟空间,如果知道开多大的空间,reserve还可以减少扩容的代价

3)resize在开辟空间的同时还会进行初始化,影响size

测试vector在VS2019下的默认扩容机制:


#include <iostream>
#include <vector>
using namespace std;void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();for (int i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed:" << sz << endl;}}
}int main()
{TestVectorExpand();return 0;
}

reserve的使用:


#include <iostream>
#include <vector>
using namespace std;void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();v.reserve(100);for (int i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed:" << sz << endl;}}
}int main()
{TestVectorExpand();return 0;
}

如果已知大概需要的空间,可以提前开好,避免频繁扩容导致效率低

vector的增删查改等 

函数说明
push_back尾插
pop_back尾删
find查找(这个是算法模块实现的,并非vector的成员函数)
insert在指定位置前插入
erase删除指定位置的数据
swap交换两个vector的数据空间
operator[ ]像数组一样访问

以上这些函数的使用相对比较简单,这里就不赘述了,直接进入重点vector的迭代器失效问题。

vector的迭代器失效问题 

vector迭代器的底层其实就是个原生指针T*,之所以会导致迭代器失效,是因为vector的底层空间发生了改变,导致T*为野指针。总结起来就是一句话:任何引起vector底层改变的操作都可能会导致vector迭代器失效。比如:insert、erase、resize、reverse等。

以erase为例:


#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v{ 1,2,3,4 };//找到指定删除的位置vector<int>::iterator pos = find(v.begin(), v.end(), 3);//删除指定位置的值v.erase(pos);cout << *pos << endl;return 0;
}

删除pos位置的元素后,pos位置之后的元素会往前挪动,没有导致底层空间的变化,理论上讲迭代器应该是还没失效的。但是,我们可以想象如果删除的pos是最后一个元素,那么pos刚好是end的位置,然而end是没有存放有效数据的,这时pos就失效了。因此,删除任意位置的元素,VS都认为该位置的迭代器失效了。 

迭代器失效的解决方法:在使用前,对迭代器重新赋值即可。

示例:

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v{ 1,2,3,4 };//找到指定删除的位置vector<int>::iterator pos = find(v.begin(), v.end(), 3);//删除指定位置的值pos = v.erase(pos);cout << *pos << endl;return 0;
}

vector的模拟实现

vector的基本框架:

namespace pcz
{template <class T>class vector{typedef T* iterator;//vector的迭代器为原生指针typedef const T* const_iterator;//const修饰的是T指向的内容~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}//……protected:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
}

end_of_storage是啥?

end_of_storage是指向vector容器当前分配的内存空间的末尾的指针或迭代器,是容量的边界

insert

在指定位置进行插入,挪动数据必不可少。


iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t old_len = pos - _start;//扩容导致迭代器失效,需要计算偏移量size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + old_len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;return pos;
}

reserve

负责开辟空间。

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();//记录size,看看size的计算方法就明白了T* tmp = new T[n];if (_start){//直接用memcpy会有问题,浅拷贝//memcpy(tmp, _start, sizeof(T) * size());//_start = tmp;for (int i = 0; i < size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}

push_back

复用insert。

iterator push_back(const T& x)
{return insert(end(), x);
}

push_front

复用insert。

iterator push_front(const T& x)
{return insert(begin(), x);
}

resize

再谈resize的作用:

1)resize用于调整vector的大小,它可以增加或减少vector中元素的数量,以达到指定的新大小。

2)如果vector的新大小大于当前大小,则会在末尾添加足够数量的新元素。、

3)如果vector的新大小小于当前大小,则vector中超出新大小的部分会被丢弃,vector的大小变为指定的新大小。

void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;return;}if(n > capacity())reserve(n);iterator it = _finish;_finish = _start + n;while (it != _finish){*it = val;++it;}
}

erase

删除指定位置的元素,伴随着数据挪动。

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;
}

pop_back

iterator pop_back()
{return erase(_finish - 1);
}

pop_front

iterator pop_front()
{return erase(_start);
}

代码


template <class T>
class vector
{
public:
typedef T* iterator;//vector的迭代器为原生指针
typedef const T* const_iterator;//const修饰的是T指向的内容~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}iterator begin()
{return _start;
}const_iterator cbegin() const
{return _start;
}iterator end()
{return _finish;
}const_iterator cend() const
{return _finish;
}void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();//记录size,看看size的计算方法就明白了T* tmp = new T[n];if (_start){//直接用memcpy会有问题,浅拷贝//memcpy(tmp, _start, sizeof(T) * size());//_start = tmp;for (int i = 0; i < size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;return;}if(n > capacity())reserve(n);iterator it = _finish;_finish = _start + n;while (it != _finish){*it = val;++it;}
}iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t old_len = pos - _start;//扩容导致迭代器失效,需要计算偏移量size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + old_len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;return pos;
}iterator push_back(const T& x){return insert(end(), x);}iterator push_front(const T& x){return insert(begin(), x);}iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;
}iterator pop_back()
{return erase(_finish - 1);
}iterator pop_front()
{return erase(_start);
}bool empty() const
{return _start == nullptr;
}protected:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};

完~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

个人视角,社会影响力:自媒体的魅力所在

随着数字化时代的到来&#xff0c;自媒体正成为信息传播领域的一场革命。个人视角与社会影响力的结合&#xff0c;赋予了自媒体独特的魅力。在传统媒体受限制的同时&#xff0c;自媒体为每个人提供了表达自己观点和思想的自由。个体的真实视角使得自媒体在信息传播中发挥着重要…

14-40 剑和诗人14 - 为什么机器学习需要合成数据

​​​​​​ 数据是人工智能的命脉。如果没有高质量、有代表性的训练数据&#xff0c;我们的机器学习模型将毫无用处。但随着神经网络规模越来越大、人工智能项目越来越雄心勃勃&#xff0c;人们对数据的需求也越来越大&#xff0c;我们面临着一场危机——现实世界的数据收集和…

如何优化 PostgreSQL 中对于复杂数学计算的查询?

文章目录 一、理解复杂数学计算的特点二、优化原则&#xff08;一&#xff09;索引优化&#xff08;二&#xff09;查询重写&#xff08;三&#xff09;数据库配置调整&#xff08;四&#xff09;使用数据库内置函数的优势 三、具体的优化方案和示例&#xff08;一&#xff09;…

华为开源自研AI框架昇思MindSpore应用案例:FCN图像语义分割

Mask R-CNN MaskRCNN是一种概念简单、灵活、通用的目标实例分割框架&#xff0c;在检测出图像中目标的同时&#xff0c;还为每一个实例生成高质量掩码。这种称为Mask R-CNN的方法&#xff0c;通过添加与现有边框检测分支平行的预测目标掩码分支&#xff0c;达到扩展Faster R-CN…

leetcode判断二分图

判断二分图 图的问题肯定要用到深度优先遍历或者广度优先遍历&#xff0c;但又不是单纯的深度优先遍历算法和广度优先遍历算法&#xff0c;而是需要在遍历的过程中加入与解决题目相关的逻辑。 题干中说了&#xff0c;这个图可能不是连通图&#xff0c;这个提示有什么作用呢&a…

shared_ptr 线程安全

为什么 shared_ptr 可以安全地在多个线程中共享&#xff1f; 循环引用 因为shared_ptr std::shared_ptr 的引用计数是线程安全的。这意味着你可以在多个线程中安全地拷贝、赋值和销毁 std::shared_ptr。然而&#xff0c;访问或修改 shared_ptr 所指向的对象时&#xff0c;需要…

昇思25天学习打卡营第20天|LSTM+CRF序列标注

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标…

明日周刊-第15期

赶在周末结束前输出一把&#xff0c;周日的晚上大家要睡个好觉哦。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 科技创新与基础设施建设 深中通道正式通车试运营 时间&#xff1a;6月30日 内容&#xff1a;国家重大工程深中通道正式通车试运营&#xff0c;标志着珠江口东…

吉时利KEITHLEY KI-488驱动和说明

吉时利KEITHLEY KI-488驱动和说明

[吃瓜教程]南瓜书第6章支持向量机

0.补充知识 0.1 超平面 定义&#xff1a; 超平面是指在&#x1d45b;维空间中&#xff0c;维度为 &#x1d45b;−1的子空间。它是分割空间的一个平面。 性质&#xff1a; n维空间的超平面 ( w T x b 0 , 其中 w , x ∈ R n ) (w^Tx_b0,其中w,x\in \mathbb R^n) (wTxb​0,其…

通过端口转发实现docker容器运行时端口更改

通过端口转发实现docker容器运行时端口更改 前言启动容器查看容器ip地址端口转发 前言 关于修改docker正在运行中容器端口&#xff0c;网上大部分分为3类: 1. 删除原有容器重新创建;2. 改配置文件;3. 在现有容器上新提交镜像&#xff0c;用新镜像起新的容器。 1和3属于同一种流…

探讨4层代理和7层代理行为以及如何获取真实客户端IP

准备工作 实验环境 IP角色192.168.1.100客户端请求IP192.168.1.100python 启动的HTTP服务192.168.1.102nginx服务192.168.1.103haproxy 服务 HTTP服务 这是一个简单的HTTP服务&#xff0c;主要打印HTTP报文用于分析客户端IP #!/usr/bin/env python # coding: utf-8import …

「技术分享」FDL对接金蝶云API取数

很多企业的ERP系统都在用金蝶云星空&#xff0c;金蝶云星空API是IT人员获取数据的重要来源&#xff0c; 常常用来生成定制化报表&#xff0c;进行数据分析&#xff0c;或是将金蝶云的数据与OA系统、BI工具集成。 通常情况下&#xff0c;IT人员需要使用Python、Java等语言编写脚…

44、tomcat安装

一、tomcat tomcat和php一样&#xff0c;都是用来处理动态页面的。 tomcat也可以作为web应用服务器&#xff0c;开源的。 php .php tomcat .jsp nginx .html tomcat 是用Java代码写的程序&#xff0c;运行的是Java的web应用程序。 tomcat的特点和功能&#xff1a; 1、s…

XSS平台的搭建

第一步&#xff1a;安装MySQL 数据库 因为xss平台涉及到使用mysql 数据库&#xff0c;在安装之前&#xff0c;先使用docker 安装mysql 数据库。 docker run --name mysqlserver -e MYSQL_ROOT_PASSWORD123 -d -i -p 3309:3306 mysql:5.6 第二步&#xff1a;安装xssplatform…

hadoop分布式中某个 节点报错的解决案例

前言 在分布式节点中&#xff0c;发现有个节点显示不可用状态&#xff0c;因此需要紧急修复。 hadoop版本 目前这套集群hadoop的版本如下&#xff1a; 集群报错详细日志&#xff1a; 1/1 local-dirs are bad: /kkb/install/hadoop-2.6.0-cdh5.14.2/hadoopDatas/tempDatas/n…

离线开发(VSCode、Chrome、Element)

一、VSCode 扩展 使用能联网的电脑 A&#xff0c;在VSCode官网下载安装包 使用能联网的电脑 A&#xff0c;从扩展下载vsix扩展文件 将VSCode安装包和vsix扩展文件通过手段&#xff08;u盘&#xff0c;刻盘 等&#xff09;导入到不能联网的离线电脑 B 中 在离线电脑 B 中安装…

快速解决找不到krpt.dll,无法继续执行代码问题

对于那些遇到计算机开机出现由于无法找到krpt.dll&#xff0c;进而无法继续执行代码问题的用户。 krpt.dll是计算机系统中与DirectX紧密相关的重要文件&#xff0c;如果它出现问题&#xff0c;可能会对一些特定的软件或游戏的运行产生影响。实际上&#xff0c;我们有多种解决该…

无需服务器,浏览器跑700+AI模型?!【送源码】

Transformers.js 是一个创新的网络机器学习库&#xff0c;它将先进的 Transformer 模型直接带入浏览器&#xff0c;无需服务器端支持。这个库与 Hugging Face 的 Python transformers 库功能对等&#xff0c;提供相似的 API 接口来运行预训练模型&#xff0c;涵盖了自然语言处理…