『C++成长记』vector模拟实现
🔥博客主页:小王又困了
📚系列专栏:C++
🌟人之为学,不日近则日退
❤️感谢大家点赞👍收藏⭐评论✍️
目录
一、存储结构
二、默认成员函数
📒2.1构造函数
📒2.2拷贝构造
📒2.3赋值重载
三、容量操作
📒3.1获取有效元素个数
📒3.2获取空间容量大小
📒3.3使用reverse扩容
四、数据访问
📒4.1下标访问
📒4.1迭代器访问
五、修正操作
📒5.1尾插数据
📒5.2尾删数据
📒5.3在任意位置插入数据
📒5.4在任意位置删除数据
🗒️前言:
vector是STL容器之一,其底层实现类似于数据结构顺序表,相当于string来说得益于泛型模板的加持使得vector可以变为任何类型,且是可以动态扩容。接下来我们就通过vector各种接口的模拟实现来深入了解vector。
一、存储结构
与string底层结构不同,vector底层空间结构为三个指针:
namespace bit
{template<class T> //模板参数Tclass vector{public:typedef T* iterator; //指针重命名为迭代器typedef const T* const_iterator;private:iterator _start; iterator _finish; iterator _end_of_storage; };
}
- _start:指向空间的起始地址
- _finish:指向最后一个数据的下一个地址,相当于_size
- _end_of_stroage:指向空间最后一个最末地址,相当于_capacity
小Tips:由于vector使用了模板,所以函数实现都在头文件中,防止因为模板导致的链接错误的问题!
二、默认成员函数
📒2.1构造函数
我们在这里实现三个版本,分别是:默认构造函数,带参构造函数和迭代器区间构造。
- 默认构造函数:初始化三个指针置空即可
- 带参构造函数:初始化n个T类型的value值在对象中
- 迭代器区间构造:通过其他容器迭代器或指针迭代插入其所有值
//默认构造函数
vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}vector () = default //强制编译器生存默认构造//构造n个T类型数据
vector(size_t n, const T& value = T()) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{reserve(n);for (int i = 0; i < n; ++i){*(_finish++) = value;}
}//迭代器区间构造
template<class InputIterator>
vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{while (first != last) //迭代器插入数据{push_back(*first);++first;}
}
注意:我们实现了带参构造函数的n为size_t类型的版本,如果我们使用带参构造函数实例化,则会发生非法间接寻址的错误!这是因为size_t是整型,实例化T数据类型也是整型,此时编译器会自动匹配最合适的构造函数,于是匹配到了迭代器区间构造。我们只要写一个n为int类型的带参构造参数去匹配,就可以解决问题。
📒2.2拷贝构造
对于拷贝构造我们要考虑深浅拷贝的问题,我们希望当一个vector对象拷贝另一个对象时新对象开辟新的空间拷贝数据,而不是两个对象共用同一块空间,否则会析构两次造成内存泄漏。
vector(const vector<T>& v)
{reserve(v.capacity());for (auto e : v){push_back(e);}
}
📒2.3赋值重载
赋值重载与拷贝构造的问题类似,也要注意深拷贝问题;区别于拷贝构造的地方在于不需要新建对象,但是需要判断是否为同一个对象避免重复开空间。
//传统写法
vector<T>& operator=(const vector<T>& v)
{if (&v != this) {clear(); reserve(v.capacity()); for (int i = 0; i < v.size(); ++i){*(_finish++) = v._start[i]; }}return *this;
}//现代写法
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}// v1 = v3
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}
三、容量操作
📒3.1获取有效元素个数
size_t size() const
{return _finish - _start;
}
📒3.2获取空间容量大小
size_t capacity() const
{return _end_of_storage - _start;
}
小Tips:vevtor是使用3个指针对空间进行管理,使用_finish(有效数据指针)减去_start(空间首地址)就能得出有效数据个数,容量同理。我们只是对数据进行访问不涉及修改,所以使用const修饰this指针。
📒3.3使用reverse扩容
void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();//获取扩容前有效数据个数T* tmp = new T[n];if(_start){//memcpy(tmp, _start, sizeof(T*) * size());for (size_t i = 0; i < oldsize; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}
}
//错误写法:_finish = _start + size() ;
小Tips:我们要用oldsize记录扩容前有效数据的个数,按照错误写法size()是用扩容前的_finish和扩容后的_start来计算的,结果是错误的。当string类型的对象要扩容时,memcpy对数据进行的是浅拷贝,两个对象指向同一块空间,会析构两次造成内存泄漏。
四、数据访问
📒4.1下标访问
下标访问是通过重载 [ ] 运算符实现的,在下标pos正确的情况下,返回当前下标字符的引用,否则assert报错。
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos) const //const引用版本,不可以修改
{assert(pos < size()); return _start[pos];
}
at 函数我们可以复用运算符[ ]来实现。 at 函数的检查手段是抛异常。
T& at(size_t pos)
{ return (*this)[pos];
}const T& at(size_t pos) const
{ return (*this)[pos];
}
📒4.1迭代器访问
typedef T* iterator;
typedef const T* const_iterator;const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }iterator begin() { return _start; }
iterator end() { return _finish; }//范围for
for (auto e : v1)
{cout << e << " ";
}
cout << endl;
五、修正操作
📒5.1尾插数据
在尾插前检查容量是否充足,空间不够扩容,然后直接插入_finish的空位下即可,_finish指针移动到下一个空位。
void push_back(const T x)
{if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;//insert(end(), x); //复用
}
📒5.2尾删数据
尾删只需要--_finish,但需要判断size是否>0。
void pop_back()
{assert(size() > 0);--_finish;
}
📒5.3在任意位置插入数据
当我们传递一个迭代器在pos位置插入数据时,可能涉及容器扩容,如果扩容,那么迭代器是旧空间的迭代器,则会导致迭代器失效,因为原有空间已经被释放,但迭代器还是指向原空间(那么就是野指针),所以我们在插入或删除后要更新迭代器。
iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;_finish++;return pos;
}
小Tips:如果要进行扩容我们用len来记录扩容前从_start到pos位置数据的个数,然后扩容后更新pos的位置。
📒5.4在任意位置删除数据
删除元素也会有迭代器失效的问题,可能会越界访问 。
iterator earse(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;it++;}--_finish;return pos; //返回删除元素位置的下一个元素的迭代器
}
🎁结语:
本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。
相关文章:

『C++成长记』vector模拟实现
🔥博客主页:小王又困了 📚系列专栏:C 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、存储结构 二、默认成员函数 📒2.1构造函数 📒2.2拷贝…...

【Mac】Charles for Mac(HTTP协议抓包工具)及同类型软件介绍
软件介绍 Charles for Mac 是一款功能强大的网络调试工具,主要用于HTTP代理/HTTP监视器。以下是它的一些主要特点和功能: 1.HTTP代理:Charles 可以作为HTTP代理服务器,允许你查看客户端和服务器之间的所有HTTP和SSL/TLS通信。 …...

LVS集群及其它的NAT模式
1.lvs集群作用:是linux的内核层面实现负载均衡的软件;将多个后端服务器组成一个高可用、高性能的服务器的集群,通过负载均衡的算法将客户端的请求分发到后端的服务器上,通过这种方式实现高可用和负载均衡。 2.集群和分布式&#…...

【RNN练习】天气预测
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 一、环境及数据准备 1. 我的环境 语言环境:Python3.11.9编译器:Jupyter notebook深度学习框架:TensorFlow 2.15.0 2. 导…...
prompt第四讲-fewshot
文章目录 前提回顾FewShotPromptTemplateforamt格式化 前提回顾 前面已经实现了一个翻译助手了[prompt第三讲-PromptTemplate],prompt模板设计中,有说明、案例、和实际的问题 # -*- coding: utf-8 -*- """ Time : 2024/7/8 …...

StarRocks分布式元数据源码解析
1. 支持元数据表 https://github.com/StarRocks/starrocks/pull/44276/files 核心类:LogicalIcebergMetadataTable,Iceberg元数据表,将元数据的各个字段做成表的列,后期可以通过sql操作从元数据获取字段,这个表的组成…...

阅读笔记——《Fuzz4All: Universal Fuzzing with Large Language Models》
【参考文献】Xia C S, Paltenghi M, Le Tian J, et al. Fuzz4all: Universal fuzzing with large language models[C]//Proceedings of the IEEE/ACM 46th International Conference on Software Engineering. 2024: 1-13.【注】本文仅为作者个人学习笔记,如有冒犯&…...
【C++】使用gtest做单元测试框架写单元测试
本文主要介绍在将gtest框架引入到项目里过程中遇到的问题。 我的需求如下: 用CMake构建项目。我要写一些测试程序验证某些功能,但是不想每一个测试都新建一个main函数。 因为新建一个main函数就要在CMakeList.txt里增加一个project,非常不方便。 于是我搜了下,C++里有没…...

Java类与对象
类是对现实世界中实体的抽象,是对一类事物的描述。 类的属性位置在类的内部、方法的外部。 类的属性描述一个类的一些可描述的特性,比如人的姓名、年龄、性别等。 [public] [abstract|final] class 类名 [extends父类] [implements接口列表] { 属性声…...
xlwings 链接到 指定sheet 从别的 excel 复制 sheet 到指定 sheet
重点 可以参考 宏录制 cell sheet.range(G4)cell.api.Hyperlinks.Add(Anchorcell.api, Address"", SubAddress"001-000-02301!A1")def deal_excel(self):with xw.App(visibleTrue) as app:wb app.books.open(self.summary_path, update_linksFalse)sheet…...

风光摄影:相机设置和镜头选择
写在前面 博文内容为《斯科特凯尔比的风光摄影手册》读书笔记整理涉及在风景拍摄中一些相机设置,镜头选择的建议对小白来讲很实用,避免拍摄一些过曝或者过暗的风景照片理解不足小伙伴帮忙指正 😃,生活加油 99%的焦虑都来自于虚度时间和没有好…...

python制作甘特图的基本知识(附Demo)
目录 前言1. matplotlib2. plotly 前言 甘特图是一种常见的项目管理工具,用于表示项目任务的时间进度 直观地看到项目的各个任务在时间上的分布和进度 常用的绘制甘特图的工具是 matplotlib 和 plotly 主要以Demo的形式展示 1. matplotlib 功能强大的绘图库&a…...
javascript设计模式总结
参考 通过设计模式可以增加代码的可重用性、可扩展性、可维护性 设计模式五大设计原则 单一职责:一个程序只需要做好一件事,如果结构过于复杂就拆分开,保证每个部分独立 开放封闭原则:对扩展开放,对修改封闭。增加需…...

gpt-4o看图说话-根据图片回答问题
问题:中国的人口老龄化究竟有多严重? 代码下实现如下:(直接调用openai的chat接口) import os import base64 import requests def encode_image(image_path): """ 对图片文件进行 Base64 编码 输入…...
【MySQL】7.MySQL 的内置函数
MySQL的内置函数 一.日期函数二.字符串函数三.数学函数四.其它函数 一.日期函数 函数名称说明current_date()当前日期current_time()当前时间current_timestamp当前时间戳(日期时间)date(datetime)截取 datetime 的日期部分date_add(date, interval d_value_type)给 date 添加…...
爬虫:Sentry-Span参数逆向
在抓某眼查数据太过频繁时会出现极验的验证码。极验的教程有很多,主要是发现在这里获取验证码的时候需要携带参数Sentry-Span。在这里记录一下逆向的主要过程,直接上补环境的代码。 window global; location {}; my_log console.log;(function () {l…...
音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现
一、引言 在上一节《音视频入门基础:H.264专题(11)——计算视频分辨率的公式》中,讲述了通过SPS中的属性计算H.264编码的视频的分辨率的公式。本文讲解FFmpeg源码中计算视频分辨率的实现。 二、FFmpeg源码中计算视频分辨率的实现…...

基于颜色模型和边缘检测的火焰识别FPGA实现,包含testbench和matlab验证程序
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 将FPGA仿真结果导入到matlab显示结果: 测试样本1 测试样本2 测试样本3 2.算法运行软件版本 vivado2019.2 …...

golang json反序列化科学计数法的坑
问题背景 func CheckSign(c *gin.Context, signKey string, singExpire int) (string, error) {r : c.Requestvar formParams map[string]interface{}if c.Request.Body ! nil {bodyBytes, _ : io.ReadAll(c.Request.Body)defer c.Request.Body.Close()if len(bodyBytes) >…...

罗技K380无线键盘及鼠标:智慧互联,一触即通
目录 1. 背景2. K380无线键盘连接电脑2.1 键盘准备工作2.2 电脑配置键盘的连接 3. 无线鼠标的连接3.1 鼠标准备工作3.2 电脑配置鼠标的连接 1. 背景 有一阵子经常使用 ipad,但是对于我这个习惯于键盘打字的人来说,慢慢在 ipad 上打字,实在是…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...