C++_vector简单源码剖析:vector模拟实现
文章目录
- 🚀1.迭代器
- 🚀2.构造函数与析构函数
- ⚡️2.1 默认构造函数vector()
- ⚡️2.2 vector(int n, const T& value = T())
- ⚡️内置类型也有构造函数
- ⚡️2.3 赋值重载operator=
- ⚡️2.4 通用迭代器拷贝
- ⚡️2.5 vector(initializer_list<T> il)
- ⚡️2.6 拷贝构造vector(const vector<T>& v)
- ⚡️2.6 析构函数~vector()
- 🚀3.内存相关
- 🚀4.获取
- 🚀5.修改
- ⚡️5.1 insert插入
- ⚡️5.2 erase删除
- ⚡️5.2 push_back尾插
- ⚡️5.3 pop_back尾删
大家好!本文会模拟一个基本的vector类,帮助我们更好的理解vector的内置函数的实现与规则。
先在.h文件声明每个需要实现的函数,需要实现的成员:
namespace bit
{template<class T>class vector{public://1.迭代器// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin();iterator end();const_iterator begin() const ;const_iterator end() const;// 2.构造函数与析构函数vector();vector(int n, const T& value = T());vector<T>& operator= (vector<T> v);template<class InputIterator>vector(InputIterator first, InputIterator last);vector(initializer_list<T> il);vector(const vector<T>& v);~vector();// 3.内存相关size_t size() const;size_t capacity() const;void reserve(size_t n);void resize(size_t n, const T& value = T());//4.获取T& operator[](size_t pos);const T& operator[](size_t pos)const;//5.修改void push_back(const T& x);void pop_back();void swap(vector<T>& v);iterator insert(iterator pos, const T& x);iterator erase(Iterator pos);private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _endOfStorage; // 指向存储容量的尾};
}
备注:private有三个成员变量,都是迭代器,_start 指向数据块的开始 ,_finish指向有效数据的尾 ,_endOfStorage指向存储容量的尾。
接下来一步一步的剖析实现:
🚀1.迭代器
typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}
备注: begin()返回首元素的指针,end()返回尾元素下一个位置的指针,当然也要多实现一个const的版本,以适应const string类型。
🚀2.构造函数与析构函数
// 2.构造函数与析构函数vector();vector(int n, const T& value = T());vector<T>& operator= (vector<T> v);template<class InputIterator>vector(InputIterator first, InputIterator last);vector(initializer_list<T> il);vector(const vector<T>& v);~vector();
⚡️2.1 默认构造函数vector()
vector() =default;
备注:vector 不需要特别的默认构造,用编译器生成的就行,我们知道,编译器在我们写了其他的构造函数时是不会生成默认构造的,所以该代码的意思是使编译器强制生成默认构造。
⚡️2.2 vector(int n, const T& value = T())
vector(int num, const T& temp = T())
{reserve(num);for (int i = 0; i < num; i++){push_back(temp);}
};
备注: reserve是扩容函数,push_back是尾插函数,后面会实现。
⚡️内置类型也有构造函数
⚡️关于(重要)
const T& temp = T():
在C++中,为了满足模板的需要,为内置类型也添加了默认构造函数
什么意思呢? 就是关于内置类型也可以这样初始化:
int i = 0;
int j(1);
int k = int();
int x = int(2);
是不是很像类的初始化的形式 ?没错,在C++中,内置类型可以像类一样传参初始化,当然就如原本的内置类型一样,不传参就是随机值,传了的那个形参就是参数的值。
这样做有什么好处呢?我们回到本函数实现的代码,如果T = int , 则:
vector(int num, const int& temp = int())
{reserve(num);for (int i = 0; i < num; i++){push_back(temp);}
};
const int& temp = int()由于int也可以用类的方式给缺省值,被赋予了一个int类型的匿名临时对象,cosnt又为这个临时对象赋予常性,就可以起别名,所以这样的语法就可以通过了。
最后,const T& temp = T()的参数形式可以满足T为自定义类型,也可以满足内置类型
⚡️2.3 赋值重载operator=
vector<T>& operator= (vector<T> v){swap(v);return (*this);
};
备注:swap是一个交换private内三个成员的函数,后面会实现。
⚡️2.4 通用迭代器拷贝
template<class InputIterator>
vector(InputIterator first, InputIterator last){reserve(last- first);while(first != last){push_back(*first);first++;}
}
备注:
- 这里使用的是函数模板,由编译器推断迭代器类型,生成对应的函数。
- 该函数的意义是支持通过其他类型的迭代器来拷贝内容,例子如下:
int main()
{string s1("123456");vector<int> test1(s1.begin(), s1.end());for (auto e : test1){cout << e<<" ";}
}
输出:49 50 51 52 53 54
这里就做到通过string的迭代器拷贝整个s1到test1
⚡️2.5 vector(initializer_list il)
vector(initializer_list<T> il){reserve(il.size());for (auto e : il){push_back(e);}
}
备注:
- 先简单介绍一下 initializer_list 是什么, initializer_list是一种特殊的标准库类型,用于在函数参数或对象构造函数中初始化列表初始化的一种方式。它允许你以简洁的方式向函数传递一组值,或者在对象的构造函数中初始化一组值,可以让函数接受不定数量的参数,而在对象构造函数中使用它可以方便地初始化成员变量。
auto test = {1,2,3,4,5};
//这里编译器推断的类型是 initializer_list
- 借助 initializer_list 我们就可以传入{1,2,3,4}这种形式的数组进行初始化。
int main()
{vector<int> test1 = {1,2,3,4};for (auto e : test1){cout << e<<" ";}
}
输出:1 2 3 4
⚡️2.6 拷贝构造vector(const vector& v)
vector(const vector<T>& temp)
{reserve(temp.capcitity());for (auto e : temp){push_back(e);}
};
备注:无。
⚡️2.6 析构函数~vector()
~vector(){delete[] _start;_start = nullptr;_end_of_storage = nullptr;_finish = nullptr;
}
备注:只用释放 头迭代器_start 就行了。
🚀3.内存相关
// 3.内存相关size_t size() const{_finish - _start;}size_t capacity() const{_end_of_storage - _start;}void reserve(size_t n){if( n > capacity()){size_t len = size();iterator tmp = new iterator[n+1];if(_start){for(int i = 0 ; i < len ; i++){tmp[i] = (*this)[i];}delete[] _start;}_start = tmp;_finish = tmp+len; _endOfStorage = tmp + n ; }}void resize(size_t n, const T& value = T()){if(n <= size()){_finish = _start +n;return;}if(n > capacity()){reserve(n);}iterator it = _finish;_finish = _start +n;while(it !=_finish ){*it = value;it++;}}
备注:
- size() 返回 vector的数据个数, capacity() 返回 vector的数据个数的容量,迭代器相减(本质是指针相减)是迭代器指向位置的距离。
- reserve()修改内存,本质上是new了一段新空间,将内容拷贝到新空间,再释放旧空间。
- 关于const T& value = T()的意思上文有讲,在2.2。
🚀4.获取
//4.获取T& operator[](size_t pos){return *(_start + x);}const T& operator[](size_t pos)const{return *(_start + x);}
备注:该函数使vector模板可以像数组一样访问元素,当然也要重载一个const版本。
🚀5.修改
//5.修改iterator insert(iterator pos, const T& x);iterator erase(Iterator pos);void push_back(const T& x);void pop_back();void swap(vector<T>& v);
⚡️5.1 insert插入
iterator insert(iterator pos, T x)
{int len = pos - _start;//记录pos的下标位置if (size() == capcitity())//判断扩容{size_t new_capcitity = capcitity() == 0 ? 4 : capcitity() * 2;reserve(new_capcitity);}iterator end = _finish - 1;//记录最后一个元素pos = _start + len;//重置pos,因为扩容后pos可能会失效while (end >= pos)//从最后一个数据开始,一个一个往后搬{*(end + 1) = *end;end--;}*pos = x;_finish++;return pos; //返回pos位置的指针
};
备注:
- 关于重置pos,因为从上文的扩容函数可知,扩容的本质是开辟新空间,所以原来的pos可能不再指向新空间的pos位置了,则导致迭代器失效(迭代器指向错误的位置), 则需要重置。
- 同时在使用过insert函数的迭代器也是存在迭代器失效的问题,所以,建议失效后迭代器不要访问。除非赋值更新一下这个失效的迭代器,严格一点的编译器会直接报错。
- 为了解决迭代器失效的问题,insert以返回值的形式返回重新生效的迭代器。
例子:
vector<int> test1 = {1,2,3,4};
int cnt = 2;
vector<int>::iterator pos = test1.begin()+1;
//错误写法,pos会失效
while (cnt--)
{test1.insert(pos, 0);
}
//实在要用的正确写法
while (cnt--)
{pos= test1.insert(pos, 0);
}
⚡️5.2 erase删除
iterator erase(iterator pos)
{if (_start){iterator head = pos;while (head < _finish){*(head) = *(head + 1);head++;}_finish--;}return pos;
};
备注:传入erase的迭代器也不推荐再使用,不同的平台的情况可能不同,可能会出现迭代器失效的问题。
⚡️5.2 push_back尾插
void push_back(const T& x)
{insert(_finish, x);
}
备注:复用insert。
⚡️5.3 pop_back尾删
void pop_back()
{erase(_finish - 1);
}
备注:复用erase。
所有的代码:
#include <assert.h>template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;//构造函数vector() = default;vector(const vector<T>& temp){reserve(temp.capcitity());for (auto e : temp){push_back(e);}};template <class other>vector(other begin, other end){reserve(end - begin);while (begin != end){push_back(*begin);begin++;}};vector(int num, const T& temp = T()){reserve(num);for (int i = 0; i < num; i++){push_back(temp);}};vector<T>& operator=(vector<T> v){swap(v);return (*this);};~vector(){delete[] _start;_start = nullptr;_end_of_storage = nullptr;_finish = nullptr;}vector(initializer_list<T> il){reserve(il.size());for (auto e : il){push_back(e);}}// reserve扩容void reserve(size_t n){if (n > capcitity()){size_t len = size();T* tmp = new T[n];if (_start){//不能用memcpy 要考虑深拷贝for (int i = 0; i < len; i++){tmp[i] = (*this)[i];}delete[] _start;}_start = tmp;_finish = tmp + len;_end_of_storage = tmp + n;}};//resize重设resizevoid resize(size_t n, const T& value = T()){/*for (int i = 0; i < n; i++){push_back(value);}*/if (n <= size()){_finish = _start + n;return;}//if (n > capcitity()){reserve(n);}iterator it = _finish;iterator _finish = _start + n;while (it != _finish){*it = value;++it;}}//迭代器iterator begin() {return _start;};iterator end() {return _finish;};const_iterator begin() const{return _start;};const_iterator end() const{return _finish;};//insert插入iterator insert(iterator pos, T x){int len = pos - _start;if (size() == capcitity()){size_t new_capcitity = capcitity() == 0 ? 4 : capcitity() * 2;reserve(new_capcitity);}iterator end = _finish - 1;pos = _start + len;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;};//push_back尾插void push_back(T x){insert(_finish, x);};//pop_back()尾删void pop_back(){erase(_finish - 1);}//capcitity容量size_t capcitity() const{return _end_of_storage - _start;};//size最后元素下标size_t size() const{return _finish - _start;};//[]T& operator[](int x){assert(x >= 0);assert(x < size());return *(_start + x);};const T& operator[](size_t x)const{assert(x >= 0);assert(x < size());return *(_start + x);};//删除iterator erase(iterator pos){if (_start){iterator head = pos;while (head < _finish){*(head) = *(head + 1);head++;}_finish--;}return pos;};//void swap(vector<T>& v){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);};
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;
};
本文就到这里,感谢你看到这里!
我知道一些人看文章喜欢静静看,不评论,但是他会点赞,这样的人,帅气低调有内涵,美丽大方很优雅,明人不说暗话,要你手上的一个点赞!
相关文章:
C++_vector简单源码剖析:vector模拟实现
文章目录 🚀1.迭代器🚀2.构造函数与析构函数⚡️2.1 默认构造函数vector()⚡️2.2 vector(int n, const T& value T())⚡️内置类型也有构造函数 ⚡️2.3 赋值重载operator⚡️2.4 通用迭代器拷贝⚡️2.5 vector(initializer_list<T> il)⚡️…...

第3章 数据链路层
王道学习 考纲内容 (一)数据链路层的功能 (二)组帧 (三)差错控制 检错编码;纠错编码 (四)流量控制与可靠传输机制 流量控制、可靠传输与滑动窗口…...

使用OrangePi KunPeng Pro部署AI模型
目录 一、OrangePi Kunpeng Pro简介二、环境搭建三、模型运行环境搭建(1)下载Ollama用于启动并运行大型语言模型(2)配置ollama系统服务(3)启动ollama服务(4)启动ollama(5)查看ollama运行状态四、模型部署(1)部署1.8b的qwen(2)部署2b的gemma(3)部署3.8的phi3(4)部署4b的qwen(5)部…...

SpringMVC 数据映射VC
从 view 层发送请求到Controller,在Controller中获取参数: 在不输入值时会报400,参数错误 在不输入值时num默认为null 没有找到对应标签名称叫nums的,输入任何值时都报400 设置required默认值为false,即使表单没有nums…...

Clickhouse Bitmap 类型操作总结—— Clickhouse 基础篇(四)
文章目录 创建 Bitmap 对象Bitmap 转换为整数数组计算总数(去重)值指定start, end 索引生成子 Bitmap指定 start 索引和数量限制生成子 Bitmap指定偏移量生成子 Bitmap是否包含指定元素两个 Bitmap 是否存在相同元素一个是否为另一个 Bitmap 的子集求最小…...

202474读书笔记|《我自我的田渠归来》——愿你拥有向上的力量,一切的好事都应该有权利发生
202474读书笔记|《我自我的田渠归来》——愿你拥有向上的力量 《我自我的田渠归来》作者张晓风,被称为华语散文温柔的一支笔,她的短文很有味道,角度奇特,温柔慈悲而敏锐。 很幸运遇到了这本书,以她的感受重新认识一些事…...

SheetJS V0.17.5 导入 Excel 异常修复 Invalid HTML:could not find<table>
导入 Excel 提示错误:Invalid HTML:could not find<table> 检查源代码 发现 table 属性有回车符 Overview: https://docs.sheetjs.com/docs/ Source: https://git.sheetjs.com/sheetjs/sheetjs/issues The public-facing websites of SheetJS: sheetjs.com…...

重学java51.Collections集合工具类、泛型
"我已不在地坛,地坛在我" —— 《想念地坛》 24.5.28 一、Collections集合工具类 1.概述:集合工具类 2.特点: a.构造私有 b.方法都是静态的 3.使用:类名直接调用 4.方法: static <T> boolean addAll(collection<? super T>c,T... el…...

OSPF扩展知识2
FA-转发地址 正常 OSPF 区域收到的 5 类 LSA 不存在 FA 值; 产生 FA 的条件: 1、5类LSA ----假设 R2为 ASBR,90/0 口工作的 OSPF 中,g0/1 口工作在非 ospf 协议或不同 ospf 进程中;若 g0/1 也同时宣告在和 g0/0 相同的 OSPF 进程…...

数据库技术基础
数据库技术基础 导航 文章目录 数据库技术基础导航一、基础概念数据库系统数据库管理系统DBMS分类数据库技术的发展数据库体系结构 二、数据模型数据模型基本概念 三、数据库的控制功能事务概述SOL中事务定义语句日志文件故障种类两个操作Undo/Redo事务故障的恢复系统故障的恢…...

这些项目,我当初但凡参与一个,现在也不至于还是个程序员
10年前,我刚开始干开发不久,我觉得这真是一个有前景的职业,我觉得我的未来会无限广阔,我觉得再过几年,我一定工资不菲。于是我开始像很多大佬说的那样,开始制定职业规划,并且坚决执行。但过去这…...

ch2应用层--计算机网络期末复习
2.1应用层协议原理 网络应用程序位于应用层 开发网络应用程序: 写出能够在不同的端系统上通过网络彼此通信的程序 2.1.1网络应用程序体系结构分类: 客户机/服务器结构 服务器: 总是打开(always-on)具有固定的、众所周知的IP地址 主机群集常被用于创建强大的虚拟服务器 客…...

Red Hat Enterprise Linux (RHEL) 8.10 发布 - 红帽企业 Linux 8 完美终结版
Red Hat Enterprise Linux (RHEL) 8.10 (x86_64, aarch64) - 红帽企业 Linux 红帽企业 Linux 8 完美终结版 请访问原文链接:Red Hat Enterprise Linux (RHEL) 8.10 (x86_64, aarch64) - 红帽企业 Linux,查看最新版。原创作品,转载请保留出处…...

.NET 直连SAP HANA数据库
前言 上个项目碰到的需求,IT部门要求直连SAP的HANA数据库,以只读的权限读取SAP部门开发的CDS视图,是个有点复杂的工程,需要从成品一直往前追溯到原材料的产地,和交货单、工单、采购订单有相当程度上的关联 IT部门要求…...
HTML <from>表单
定义:<form>元素定义了一个表单,用户可以在表单中输入数据,这些数据可以被提交到服务器。 属性: action:指定表单提交时的目标URL(服务器端脚本的地址)。 method:定义提交表…...

Wpf 使用 Prism 实战开发Day28
首页汇总方块点击导航功能 点击首页汇总方块的时候,跳转到对应的数据页面 step1: 在IndexViewModel 中,给TaskBar 里面Target 属性,赋上要跳转的页面 step2: 创建导航事件命令和方法实现 step3: 实现导航的逻辑。通过取到 IRegionManager 的…...
如何让一个普通用户可以读写某个目录
循环设置这个目录以及上面每一级目录的读取和执行权限 sudo chmod -R orx /opt/software/yourdir 然后设置指定用户user1可以读写这个目录 sudo setfacl -Rm u:user1:rwx /opt/software/yourdir 读取acl sudo getfacl -R /opt/software/yourdir -R 是循环读取子目录和文件的意思…...
知识笔记——jieba分词初探
1. 简介 jieba 是python中一个非常好用的 中文分词组件,但它并不是只有分词这一个功能,还提供了很多在分词之上的算法,如关键词提取、词性标注等。 安装方式: pip install jieba2. 分词 支持 3 种分词模式:精确模式…...

GPT-4o:人工智能新纪元的开端
引言 近年来,人工智能领域的发展日新月异,特别是在自然语言处理(NLP)领域,各种生成预训练模型不断推陈出新。自OpenAI发布GPT-3以来,生成预训练模型在文本生成、语言理解等任务中展现了强大的能力。近期&a…...

探索AI去衣技术中的反射应用
在当今数字时代,人工智能(AI)技术的飞速发展已经渗透到了我们生活的方方面面。其中,图像处理和计算机视觉作为AI的重要分支,正不断推动着创新应用的边界。今天,我们要探讨的是一个颇具争议但又技术上颇为有…...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...