『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 上打字,实在是…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...