vector(二)vector模拟实现
vector成员变量是三个迭代器 vector的迭代器底层与string相同是使用 指针实现的 使用的是类模版T*指针
template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;
这里使用的是模版类 同时三个私有成员都是由指针的迭代器创建的
其中 _start是指向容器第一个元素位置 _finish是指向容器有效元素的最后一个位置的下一位
_end_of_storage 是指向容器整个空间的最后一个位置
vector<int>v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
在没写构造函数时会自动生成默认的构造函数
void push_back(const T& x){if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}
push_back尾插 首先是判断是否扩容 之后将要插入的参数放在_finish的位置 之后++_finish 完成尾插 这里的扩容需要reserve函数
void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * oldsize);//对任意类型进行的都是浅拷贝for (size_t i = 0; i < oldsize; i++)tmp[i] = _start[i];//这里的赋值使用的是string的赋值//std::swap(tmp[i], start[i]);delete[] _start;}_start = tmp;_finish = tmp + oldsize;_end_of_storage = _start + n;}}
这里的size使用来查看容器中有效数据长度的
size_t capacity()
{return _end_of_storage - _start;
}size_t size()
{return _finish - _start;
}
首先必须先拿到size有效长度 并且赋值给一个变量 这样就可以防止后面size()出错
如果直接使用size()函数进行查找 那么在后面当_start重新指向新空间时 这是_finish 和_end_of_storage 还是指向旧的空间 这是通过size和capacity函数进行访问时 就会发生错误
所以要使用变量对size进行赋值存储 防止之后出错
对于vector容器进行遍历需要用到 []重载 以及迭代器begin和end
iterator begin()
{return _start;
}iterator end()
{return _finish;
}
_start和_finish所指向的位置与begin和end相同所以直接返回这两个指针就好
T& operator[](size_t i)
{assert(i < size());return _start[i];
}
[]返回的是对应下标的值 这里的访问不仅可以查看 还可以修改当前位置的值
在写好迭代器和[]重载后即可利用三种方式对vector容器进行遍历
void test1()
{vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (size_t i = 0; i < v1.size(); i++)//.h文件在运行时不会被编译而是在预处理阶段就会被展开(规则一){//规则二 编译器只会向上查找cout << v1[i] << " ";}cout << endl;vector<int>::iterator it = v1.begin();//这是使用int*作为类型也是可以通过的 因为这里的本质就是int* 但是这里最好是写成标准形式 方便调用其他命名空间和库时可以直接使用while (it != v1.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v1){cout << e << " ";}cout << endl;
}
接下来模拟对容器插入和删除元素 insert和erase
iterator insert(iterator pos, const T& x)//出现随机值问题 扩容会导致pos迭代器失效(野指针)
{//扩容后续重新计算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;
}
这里首先通过assert对pos进行检查是否合法 之后就要检查空间是否足够 空间不够则会先进行扩容 之后就是挪动数据 为插入的元素腾出空间 之后在pos位置插入对应元素 完成插入
这里在测试时可能会出现随机值问题 这是因为出现的pos迭代器失效的情况 原理是pos迭代器指向原本的空间 但是如果发生扩容 就会深拷贝 导致pos迭代器还是指向已经消失的空间 没有指向对应的新空间的对应位置上 解决方法 就是使用len记录pos在空间上距离_start的距离 之后在扩容结束后 使用新的_start对pos迭代器进行更新 这样就能 保证迭代器在函数体内正常使用
由于我们的参数是传值的 也就是说我们函数体外面的调用的这个pos迭代器也会失效 正常情况下是不能使用 如果非要使用 可以更改insert的返回类型 返回更新后的pos迭代器 对外面的pos进行一次更新 保证其可以使用
iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;it++;}--_finish;return pos;}
erase是删除pos位置的元素并且重新分配空间 首先通过assert来对pos迭代器进行是否合法检查 之后便是挪动数据 更新_finish完成删除 这里的pos也是有可能会发生失效的 如果刚好删除的是最后一个队尾元素 那么在删除完成后迭代器会处于失效 而在一些比遍历情况下 删除即使处于队中的迭代器也会发生失效
void test2(){bit::vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;v1.insert(v1.begin(), 0);for (auto e : v1){cout << e << " ";}cout << endl;v1.erase(v1.begin());for (auto e : v1){cout << e << " ";}cout << endl;v1.insert(v1.begin() + 2, 50);for (auto e : v1){cout << e << " ";}cout << endl;//只有string提供了find 而vector list等都是没有直接提供find 但是他们可以使用算法algorithm库中的find函数(复用)}
void test3()
{bit::vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);int x;cin >> x;//如果可以找到x 那么就在x的位置前面插入 如果找不到 那就不插入bit::vector<int>::iterator pos = find(v1.begin(), v1.end(), x);//insert以后这个实参会不会失效if (pos != v1.end())//也是会发生失效的 即使函数内部已经预防进行调整 但是形参改变 不会影响实参 依旧是扩容就会失效{//因为不知道什么时候扩容什么时候失效 所以迭代器的建议是 不要使用可能会失效的迭代器 也不能在参数上加引用 因为begin返回的是传值返回 会产生拷贝后临时变量 具有常性 此时引用会导致权限的放大pos = v1.insert(pos, 1000);//通过函数返回值 来更新一下pos迭代器防止失效 也就是建议失效迭代器不要访问 除非更新一下它}for (auto e : v1){cout << e << " ";}cout << endl;}
void test4()
{vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);int x;cin >> x;vector<int>::iterator pos = find(v1.begin(), v1.end(), x);if (pos != v1.end()){pos = v1.erase(pos);//可能是由删除后缩容引起的迭代器失效//也有可能在删除最后一个成员会导致非法访问if (pos != v1.end())//这里为了访问到队尾的最后一个元素cout << *pos << endl;//这里erase也是通过返回值来更新迭代器从而保证迭代器不会失效}cout << typeid(pos).name() << endl;//不论是在函数执行中形参失效 还是函数执行完成后实参都会失效 这里的vs做的比较绝 在执行完成后会统一将迭代器的类型做出修改 一旦访问这个类型的迭代器就会报错警告for (auto e : v1){cout << e << " ";}cout << endl;
}
void test5()
{vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);v1.push_back(6);//如果最后一个是偶数 需要删除的也是可以的 在删除后 pos指向了_finish while循环检查之后就会直接结束了vector<int>::iterator it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){it = v1.erase(it);//每次删除后都会导致当前迭代器的失效 需要进行手动更新}else{it++;//不要存侥幸 如果偶尔在一个编译器上能跑 你要考虑这串代码是否在其他编译器上都能跑}}for (auto e : v1){cout << e << " ";}cout << endl;
}
vector() = default;//这里可以强制生成默认构造;vector(const vector<T>& v)
{for (auto e : v){push_back(e);}
}
这里是拷贝构造 通过遍历方式依次将内容拷贝给this指向的容器
在写完拷贝构造后 由于拷贝构造也是构造的一种 所以会被当做默认构造 所以通过default强行生成默认构造
void test6(){vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);vector<int> v2(v1);//如果不写 生成的是默认的浅拷贝 值拷贝就会导致这里析构两次的问题for (auto e : v2)//没有写 会自动构造 使用的是缺省值 但是没有写构造 写了拷贝构造 构造的规则是 拷贝构造也算构造 这时我们需要自己去写构造了{cout << e << " ";}cout << endl;vector<int>v3;v3.push_back(10);v3.push_back(20);v3.push_back(30);vector<int>v4;v4 = v3;for (auto e : v4){cout << e << " ";}cout << endl;}
//迭代器区间初始化
template <class InputIterator>//一个类模版的成员函数还可以写成函数模版
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
通过这个构造可以直接通过迭代器选择范围进行拷贝
void test7(){vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);vector<int> v2(v1.begin(),v1.end());//这是在没写迭代器区间初始化也能做到的for (auto e : v2){cout << e << " ";}cout << endl;vector<int> v3(v1.begin()+2, v1.end());//这是只有迭代器区间初始化后才能做到的for (auto e : v3){cout << e << " ";}cout << endl;string s1("hello");vector<int> v4(s1.begin() +1, s1.end());//char类型转换成整型后使用的是ascll码值for (auto e : v4)//这里因为是模版 谁的迭代器都可以使用{cout << e << " ";}cout << endl;}
vector(size_t n, const T& val = T())//这个地方由于是T模版 所以不能用0去初始化 这里是匿名对象
{//内置类型没有默认构造 这里是默认c++进行了升级reserve(n);for (size_t i = 0;i <n;i++){push_back(val);}
}vector(int n, const T& val = T())//这个地方由于是T模版 所以不能用0去初始化 这里是匿名对象
{//内置类型没有默认构造 这里是默认c++进行了升级reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}
void test8()
{int i = 0;int j(1);int k = int();int x = int(2);string s("hello");vector<string> v1(10);//不给第二个参数 走的是缺省值vector<string> v2(10, "xxx");//这里是隐式类型转换vector<string>v3(10,s);for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;for (auto e : v3){cout << e << " ";}cout << endl;vector<int>v4(10, 1);//这里会调用错误的迭代器区间函数 编译器会认为那个编译器更加匹配for (auto e : v4)//也就是上述两个构造同时存在导致的{//解决问题 在输入是手动改为uint//第二就是写一个重载的版本cout << e << " ";}cout << endl;}
vector(initializer_list<int> e2)
{reserve(e2.size());for (auto e : e2){push_back(e);}}
void test9()
{vector<int>v2 ({ 1,2,3,4,5,6,7,8,9 });//这里参数数量不固定 即便是上面的也只能写一个或者两个 不能多个,直接构造for (auto e : v2){cout << e << " ";}cout << endl;vector<int>v1 = {1,2,3,4,5,6,7,8,9};//隐式类型转换for (auto e : v1){cout << e << " ";}cout << endl;auto e = { 1,2,3 };initializer_list<int> e2 = {1,2,3,5,7,8,9};//这两者是等价的
}
在c++11中添加一些关于隐式类型转化的新形式
在之前的隐式类型转换中最多只有2个进行隐式类型转换 在使用上述的构造后可以进行多个数据进行隐式类型转换
void test10()
{vector<string>v1;v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");v1.push_back("11111111111111");for (auto e : v1){cout << e << " ";}cout << endl;
}
这里连续超过四个尾插会造成崩溃 原因是第五个插入是会进行扩容 而扩容使进行拷贝时如果使用的memcpy 由于memcpy对任何都是浅拷贝 之后删除_start会导致tmp指向的同一片空间被删除 这样就会导致崩溃 所以时候用swap或者赋值对内容进行拷贝
相关文章:

vector(二)vector模拟实现
vector成员变量是三个迭代器 vector的迭代器底层与string相同是使用 指针实现的 使用的是类模版T*指针 template<class T> class vector { public:typedef T* iterator;typedef const T* const_iterator; private:iterator _start nullptr;iterator _finish nullp…...

【Canvas与电脑桌面】用六角回旋镖铺满一个平面(1920*1080)
【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>六角回旋镖桌面1920x1080</title><style type"text/cs…...

创游系列开心娱乐完整组件
别人分享的一套东西,是个不错的娱乐源码,里面包含了很多小游戏。可以创建房间。 没测试自行研究吧,内含搭建教程。 代码免费下载:百度网盘...

高效驱动之选 ——KP85211ASGA 半桥栅极驱动器 内置互锁死区
KP85211A是一款 225V 耐压,具有 1A 拉电流和 1.5A 灌电流能力的半桥栅极驱动器,专用于驱动功率MOSFET或IGBT。采用高压器件工艺技术,具有良好的电流输出及出色的抗瞬态干扰能力。可保证开关节点 VS 瞬态 -7V 情况下系统正常工作。可支持开关节…...

建投数据获批安全生产许可证
9月1日,建投数据成功获批由北京市住房和城乡建设委员会核发的《安全生产许可证》。该资质的获得,是建投数据能力与实力的展现,更是对其企业规模、管理水平、项目业绩等的肯定。 《安全生产许可证》是国家为了加强安全生产监督管理,…...

MCU9.reg52.h的介绍
1.引用头文件的两种方式 #include <reg52.h> #include "reg52.h" 区别:优先搜索的位置不同! 在keil软件中 #include <reg52.h> 优先搜索软件安装的INC文件夹 #include "reg52.h" 优先搜索当前工程文件夹下的头文件,如果没有,则在软件安装的…...

Python知识点:如何使用Python进行二维码生成与识别
在Python中,生成和识别二维码可以使用不同的库来实现。最常用的库包括 qrcode 和 pyzbar。以下是如何使用这些库来生成和识别二维码的示例: 1. 生成二维码 你可以使用 qrcode 库来生成二维码。首先,你需要安装它: pip install …...

跨域问题(CORS)
文章目录 介绍解决一、添加跨域头,允许跨域1.后端配置CORS策略(4种方法)2.配置nginx 二、代理 介绍 跨域资源共享(CORS, Cross-Origin Resource Sharing)是浏览器的一个安全机制,用来防止来自一个域的网页对另一个域下的资源进行…...

评测AI写毕业论文软件排行榜前十名的网站
在当今信息爆炸的时代,AI智能写作工具已经成为我们写作过程中的得力助手。特别是对于学术论文的撰写,这些工具不仅能够提高写作效率,还能帮助用户生成高质量的文稿。以下是五款值得推荐的AI智能写论文软件,其中特别推荐千笔-AIPas…...

发邮件格式
邮件作为一种正式的沟通方式,其格式通常需要遵循一定的规范。 尊敬的xx:(无缩进) 您好!(缩进两个字符) 正文(缩进两个字符)xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xx…...

解锁Web3.0——Scaffold-eth打造以太坊DApp的终极指南
🚀本系列文章为个人学习笔记,目的是巩固知识并记录我的学习过程及理解。文笔和排版可能拙劣,望见谅。 目录 前言 一、快速部署 1、前期准备: 2、安装项目: 二、配置部署运行环境 1、初始化本地链:…...

机器学习之监督学习(四)决策树和随机森林
机器学习之监督学习(四)决策树和随机森林 0. 文章传送1. 决策树 Decision Tree案例引入构建过程 2. 随机森林 Random Forest3. 决策树 vs 神经网络4. 代码实现手写版本sklearn版本 5. 案例Iris数据集介绍实验代码 0. 文章传送 机器学习之监督学习&#…...

Sky Takeaway
软件开发整体介绍 软件开发流程 角色分工 软件环境 苍穹外卖 项目介绍 定位:专门为餐饮企业定制的一款软件产品 技术选型 前端环境搭建 阅读readme文档 nginx.exe放入无中文目录运行并启动 后端环境搭建 项目结构 Nginx反向代理 优点 配置 Nginx反向代理 负…...

JavaScript 模板字符串
模板字符串(Template Literals)是 JavaScript ES6 引入的一项功能,它让字符串的处理变得更加灵活和直观。以下是对模板字符串的详细介绍,包括它的基本特性、用法以及一些高级用法。 一 基本特性 1. 多行字符串 模板字符串允许创…...

模拟new关键字时产生的问题,求解答!
目的:编写函数myNew来模拟new关键字 首先,我们知道new关键字的工作: 1.产生一个新对象 2.将新对象的__proto__属性指向构造函数的prototype属性 3.将新对象赋值给构造函数的this 4.执行构造函数中的代码 函数实现如下: fun…...

SpringBoot2:请求处理原理分析-接口参数解析原理(argumentResolvers)
一、知识回顾 我们知道,接口的参数,一般都要配上注解来一起使用。 不同的参数注解,决定了传参的方式不同。 为什么会这样? 如果让你设计接口参数解析,你会怎么做? 首先,我们知道方法参数是形…...

java实现文本相似度计算
需求 **文本推荐:**有多个文本字符串,如何设计一个简单的统计方法(从词频的角度设计),来计算出多个文本字符串两两之间的相似度,并输出大于指定相似度阈值的文本 分析理解 使用Java实现文本相似度计算的…...

基于无人机边沿相关 ------- IBUS、SBUS协议和PPM信号
文章目录 一、IBUS协议二、SBUS协议三、PPM信号 一、IBUS协议 IBUS(Intelligent Bus)是一种用于电子设备之间通信的协议,采用串行通信方式,允许多设备通过单一数据线通信,较低延迟,支持多主机和从机结构&a…...

django学习入门系列之第十点《A 案例: 员工管理系统4》
文章目录 6 部门管理(原始方式)6.6 添加界面的导入(数据库)6.7 删除按键的应用6.8 编辑按键的应用6.81 传值的另一种方式 6.9 提交按键的应用 往期回顾 6 部门管理(原始方式) 6.6 添加界面的导入ÿ…...

【2024】Math-Shepherd:无需人工注释即可逐步验证和强化法学硕士。
搜索词: Math-shepherd: Verify and reinforce llms step-by-step without human annotations P Wang, L Li, Z Shao, R Xu, D Dai, Y Li, D Chen, Y Wu, Z Sui Proceedings of the 62nd Annual Meeting of the Association for …, 2024•aclanthology.org 摘要…...

[苍穹外卖]-08微信支付详解
地址簿管理 分析需求: 查询地址列表/新增地址/修改地址/删除地址/设置默认地址/查询默认地址 接口设计 新增地址接口 查询用户所有的地址接口 查询默认地址接口 根据id修改地址接口 根据id删除地址接口 根据id查询地址接口 设置默认地址接口 数据库设计: 收货地址簿(address_…...

教你五句在酒桌上和领导说的话语
1、今天很荣幸能和领导一起吃饭,我敬领导一杯希望领导工作顺利身体健康!生意兴隆!2、我敬领导一杯感谢领导平时对我的关照先干为敬!3、谢谢领导这次给我这个机会我一定会好好把握的请领导放心我一定会好好工作绝对不辜负领导对我的期望4.领导能来这里我们感到非常骄…...

景联文科技:专业图像采集服务,助力智能图像分析
景联文科技是专业数据服务公司,致力于为人工智能企业提供从数据采集、清洗到标注的全流程解决方案。协助客户解决AI开发过程中数据处理环节的关键问题,助力企业实现智能化转型。 1.多样化的图像采集服务 景联文科技提供多样化的图像采集服务,…...

QT QTcpSocket作为客户端
前言 QTcpSocket是Qt提供的关于TCP网络通信的类。QTcpSocket是一个异步的类,能够非阻塞式发送和接收数据。QTcpSocket内部封装了网络通信相关细节,对外提供便利的接口去帮助开发人员实现简历连接、断开连接、数据收发。 主要内容 基本使用方式 项目文…...

【系统架构设计师-2023年】综合知识-答案及详解
更多内容请见: 备考系统架构设计师-核心总结索引 文章目录 【第1~2题】【第3题】【第4~5题】【第6题】【第7题】【第8题】【第9题】【第10~11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第21~22题】【第23题】【第24~…...

树莓派3B点灯(1)-- 四种方法
先做个简单一丢丢的吧。。。正好最近工作也要用这个。这次直接给够四种方法,给好给满。分别是Python点,用户空间配置GPIO点,设备树配置内核Leds驱动点,自己写驱动点。 用的板子是树莓派3B,GPIO 26口,蓝光L…...

Android解析XML格式数据
文章目录 Android解析XML格式数据搭建Web服务器Pull解析方式SAX解析方式 Android解析XML格式数据 通常情况下,每个需要访问网络的应用程序都会有一个自己的服务器,我们可以向服务器提交数据,也可以从服务器上获取数据。不过这个时候就出现了…...

数学建模笔记—— 灰色关联分析[GRA]
数学建模笔记—— 灰色关联分析[GRA] 灰色关联分析(GRA)1. 相关概念1.1 灰色系统1.2 什么是关联分析?1.3 灰色关联分析 2. 关联分析步骤3. 典型例题3.1 关联分析例题3.2 灰色关联综合评价 4. python代码实现4.1 关联分析4.2 灰色关联综合评价 灰色关联分析(GRA) 1.…...

ICM20948 DMP代码详解(13)
接前一篇文章:ICM20948 DMP代码详解(12) 上一回完成了对inv_icm20948_set_chip_to_body_axis_quaternion函数第2步即inv_rotation_to_quaternion函数的解析。回到inv_icm20948_set_chip_to_body_axis_quaternion中来,继续往下进行…...

【论软件需求获取方法及其应用】
摘要 2023 年 3 月,我所在的公司承接了某油企智慧加油站平台的建设工作。该项目旨在帮助加油站提升运营效率、降低运营成本和提高销售额。我在该项目中担任系统架构设计师,负责整个项目的架构设计工作。 本文以该项目为例,详细论述软件需求获…...