郑州短视频运营/南宁网站seo大概多少钱
目录
- 前言
- vector核心框架模拟实现
- 1. 前期准备
- 2. 构造和销毁
- 补充: 隐式类型转换和多参数构造的区别
- 3. 迭代器相关
- 4. 容器相关
- 补充: memcpy拷贝问题
- 5. 元素访问
- 6. vector的修改
- 测试代码
- 总结
前言
本文重点模拟实现vector的核心接口, 帮助我们更好的理解底层逻辑, 以及对vector的深度剖析.
博客主页: 酷酷学!!! 期待关注~
正文开始
vector核心框架模拟实现
1. 前期准备
首先, 可以查看到STL源码, 底层vector的实现并不是我们通常顺序表那样定义成员变量, 而是通过迭代器也就是指针进行实现, 那么我们也按照STL来进行模拟实现.
T* _a;
size_t _size;
size_t _capacity;
#pragma once#include<iostream>
#include<assert.h>
using namespace std;namespace my
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//核心接口的实现private:iterator _start; //指向数据块开始iterator _finish; //指向有效数据的尾iterator _endOfStorage; //指向存储容量的尾};
}
2. 构造和销毁
- 默认无参构造
//默认无参构造vector():_start(nullptr), _finish(nullptr), _endOfStorage(nullptr){}
默认的无参构造, 因为在声明的时候我们并没有给缺省值, 所以我们也可以直接在初始化列表进行初始化.
- 迭代器区间初始化
//迭代器区间初始化//若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)//只能是vector的迭代器//重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}
- 初始化n个相同的值
//n个相同的值,使用默认的构造函数进行初始化, //对于内置类型,C++对这方面也进行了支持//vector(size_ n, const T& value = 0)//这里缺省值不能给0,如果对于自定义类型就有问题了vector(size_t n, const T& value = T())//匿名对象//相当于缺省值给了一个临时的匿名对象:_start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(n);while (n--){push_back(value);}}/** 理论上讲, 提供了vector(size_t n,const T& value = T())之后,* vector(int n,const T& value = T())就不需要提供了,但是对于:* vector<int> v(10,5);* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型* 就不会走vector(size_t n,const T& value = T())这个构造方法* 最终选择的是:vector(InputIterator first, InputIterator last)* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int* 但是10和5根本不是一个区间,编译时就报错了* 故需要增加该构造方法*/vector(int n, const T& value = T()): _start(new T[n]), _finish(_start + n), _endOfStorage(_finish){for (int i = 0; i < n; ++i){_start[i] = value;}}
这里可能不太理解为什么 const T& value = T() 这个缺省值要给T(), 要给默认构造函数, C++对于内置类型也进行了升级, 内置类型也可以使用构造初始化, 所以这个值, 不管自定义类型还是内置类型都可以适用
// C++内置类型进行了升级,也有构造int i = 0;int j(1);int k = int();int x = int(2)
有时候, 我们会发现有些代码使用vector是这样写的
vector<int> v{ 1, 2, 3, 4 };
查看C++文档, 可以发现这是C++11的新语法让vector用起来更加方便, 使用<initializer_list>这个类进行初始化.
initializer_list中有两个成员变量. begin指针和end指针, 记录了列表的开始位置和结尾位置, 记录列表这段区间, 进行初始化.
- initializer_list进行初始化
vector(initializer_list<T> il):_start(nullptr),_finish(nullptr),_endOfStorage(nullptr){reserve(il.size());for (auto e : il){push_back(e);}}
首先扩容, 然后遍历il, 将里面的值尾插到vector.
- 拷贝构造
//拷贝构造vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_endOfStorage(nullptr){reserve(v.capacity());iterator it = begin();const_iterator vit = v.cbegin();while (vit != v.cend()){*it++ = *vit++;}_finish = it;}//vector(const vector<T>& v)// :_start(nullptr)// ,_finish(nullptr)// ,_endOfStorage(nullptr)//{// reverse(v.capacity());// for (auto e : v)// {// push_back(e);// }//}
- 赋值运算符重载
void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}//赋值运算符重载// v1 = v3vector<T>& operator=(vector<T> v){swap(v);return *this;}
- 析构函数
//析构函数~vector(){if (_start){delete[] _start;_start = _finish = _endOfStorage = nullptr;}}
补充: 隐式类型转换和多参数构造的区别
class A{public:A(int a1 = 0):_a1(a1), _a2(0){}A(int a1, int a2):_a1(a1),_a2(a2){}private:int _a1;int _a2;};void test()
{//单参数构造和多参数对象隐式类型转换A aa1(1); //这里是单参数构造A aa2 = 1; //这里是隐式类型转换A aa3(1,1); //这里是多参数构造A aa4 = {1,1}; //这里是多参数隐式类型转换A aa5{1,1}; //这里是多参数隐式类型转换省略=, 一般不要这种写法A aa6{1};A aa7 = {1}; //这两个是C++为了想让{}进行统一, 所以单参数也可以使用{}进行构造, 比较冗余,一般不要这样写/////自定义类型动态开辟调用构造函数A* p1 = new A;//无参构造A* p2 = new A(2); //单参数传参构造A* p3 = new A(2,3); //多参数传参构造A* p1 = newA[10]; //连续申请10个空间,无参构造A aa1(1);A aa2(2);A aa3(3);A *p2 = new A[10]{aa1,aa2,aa3};//拷贝构造,其余为0A* p3 = new A[10]{1,2,3,4,{6,7}};//也可以直接写,进行隐式类型转换///这里的隐式类型转换,跟上面不一样,这里参数个数不固定vector<int> v1({ 1,2,3,4,5,6 });vector<int> v2 = { 10, 20, 30};//()可以省略for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;vector<A> v3 = { 1, A(1), A(2,2), A{1}, A{2,2}, {1}, {2,2} };//这个是首先创建一个存储A的列表然后进行构造}
3. 迭代器相关
这里分别对应静态和非静态vector
iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}
4. 容器相关
- size和capacity
size_t size() const{return _finish - _start;}size_t capacity() const{return _endOfStorage - _start;}
- 修改空间容量, 默认增容
void reserve(size_t n){if (n > capacity()){size_t oldSize = size();//1.开辟新的空间T* tmp = new T[n];//2.拷贝元素/*if (_start){memcpy(tmp, _start, sizeof(T) * size);}*///不可以用memcpy拷贝if (_start){for (size_t i = 0; i < oldSize; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldSize;_endOfStorage = _start + n;}}
补充: memcpy拷贝问题
- memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中.
- 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。(比如拷贝int类型既高效又不会出错, 那如果vector里面存储的都是一个个的指针类型呢? 比如string类)
比如下面这段代码
vector<string> v1;v1.push_back("111111111111111111");v1.push_back("111111111111111111");v1.push_back("111111111111111111");v1.push_back("111111111111111111");v1.push_back("111111111111111111");for (auto e : v1){cout << e << " ";}cout << endl;
当插入第五个值的时候, vector需要进行扩容, memcpy会继续拷贝, 但这是浅拷贝, 将_start里面的值拷贝到tmp中, 此时tmp中成员_str也指向了原来的那段空间, 当_start释放后, 区间就会被销毁, 此时tmp里面的内容就指向了野指针.
而这里使用赋值拷贝, 会调用我们的赋值拷贝函数, 就不会出现浅拷贝的问题了.
结论: 如果对象中涉及到资源管理时, 千万不能使用memcpy进行对象之间的拷贝, 因为memcpy是浅拷贝, 否则可能会引起内存泄漏甚至程序崩溃
- rsize()函数
void resize(size_t n, const T& value = T()){//1.如果n小于当前的size,则数据缩小到nif (n <= size()){_finish = _start + n;return;}//2.空间不够则增容if (n > capacity())reserve(n);//3.将size扩大到n,并使用value填充后面的值iterator it = _finish;_finish = _start + n;while (it != _finish){*it = value;++it;}}
5. 元素访问
T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}T& front(){return *_start;}const T& front() const{return *_start;}T& back(){return *(_finish - 1);}const T& back() const{return *(_finish - 1);}
6. vector的修改
void push_back(const T& x){insert(end(), x);}void pop_back(){erase(end() - 1);}iterator insert(iterator pos, const T& x){assert(pos <= _finish);assert(pos >= _start);//空间不够先进性增容if (_finish == _endOfStorage){size_t newcapacity = (capacity() == 0) ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + size();}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}//返回删除数据的下一个数据,//方便解决迭代器失效的问题iterator erase(iterator pos){iterator begin = pos + 1;while (begin != _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}
测试代码
void TestVector1()
{my::vector<int> v1;my::vector<int> v2(10, 5);int array[] = { 1,2,3,4,5 };my::vector<int> v3(array, array + sizeof(array) / sizeof(array[0]));my::vector<int> v4(v3);for (size_t i = 0; i < v2.size(); i++){cout << v2[i] << " ";}cout << endl;
}void TestVector2()
{my::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;cout << v.front() << endl;cout << v.back() << endl;cout << v[0] << endl;for (auto e : v){cout << e << " ";}cout << endl;v.pop_back();v.pop_back();for (auto e : v){cout << e << " ";}cout << endl;v.insert(v.begin(), 0);for (auto e : v){cout << e<<" ";}cout << endl;v.erase(v.begin() + 1);for (auto e : v){cout << e << " ";}cout << endl;
}
总结
本篇对vector的核心接口进行了模拟实现和探究, C++这门语言本身就比较偏向底层, 希望能够帮助大家进一步理解底层逻辑以及实现思路, 感谢三连!!!
完
相关文章:

vector深度剖析及模拟实现
目录 前言vector核心框架模拟实现1. 前期准备2. 构造和销毁补充: 隐式类型转换和多参数构造的区别 3. 迭代器相关4. 容器相关补充: memcpy拷贝问题 5. 元素访问6. vector的修改测试代码 总结 前言 本文重点模拟实现vector的核心接口, 帮助我们更好的理解底层逻辑, 以及对vecto…...

spring 中包自动扫描之 component-scan 解析
在 spring 中,为简化 bean 的配置,在 spring-context 模块下提供了包的自动扫描功能,将配置的包及其子包下的所有符合条件的类都注册到 BeanFactory 中。下面来看下具体是怎么实现的。 配置 <context:component-scan base-package"…...

【C语言】Linux 飞翔的小鸟
【C语言】Linux 飞翔的小鸟 零、环境部署 安装Ncurses库 sudo apt-get install libncurses5-dev壹、编写代码 代码如下: bird.c #include<stdio.h> #include<time.h> #include<stdlib.h> #include<signal.h> #include<curses.h>…...

mcasttest-tool组播检测工具
作者:广大 检测组播 mcasttest-tool是oracle组播检测工具,组播是oracle 11.2.0.2开始的新功能。 1、上传mcasttest工具解压并授权 [rootrac1 soft]# cd /u01/soft/ [rootrac1 soft]# tar -xvf mcasttest.tgz[rootrac1 soft]# chown -R grid:oinstall…...

ncnn 库编译的一些问题,使用交叉编译
一开始的问题是编译完程序,但是部分工具没有编译出来。 主要的问题是: 1. ncnn2in8 程序没有编译出来:主要原因应该是cmakelists.txt文件中对于的模块没打开on,或者这个模块没加进去编译: 添加以下 -DNCNN_BUILD_EXAMPLESON -…...

Python基础教程(一)
1.编程基础 1.1标识符 标识符是变量、函数、模块和其他对象的名称。Python中标识符的命名不是随意的,而是要遵守一定的命名规则,比如说: 1、标识符是由字母 (A~Z 和 a~z) 、下划线和数字组成,但第一个字符不 能是数字。 2、标识符不…...

基于C51和OLED12864实现贪吃蛇小游戏
引言 在微电子技术飞速发展的今天,单片机作为智能控制的核心,广泛应用于各种电子设备中。C51系列单片机以其高效、稳定的特性,成为众多电子爱好者和工程师的首选平台。而OLED显示屏以其轻薄、低功耗、响应速度快等优点,在显示设备…...

JVM性能调优全指南:高流量电商系统的最佳实践
1.G1(Garbage-First) 官网: G1 Garbage Collection G1收集器是Java 7中引入的垃圾收集器,用于替代CMS(Concurrent Mark-Sweep)收集器。它主要针对大内存、多核CPU环境下的应用场景,具有以下特点: 分代收集:G1仍然保留了分代的概念,但新生代和老年代不再是物理隔离的,…...

前端常见场景、JS计算精度丢失问题(Decimal.js 介绍)
目录 一. Decimal.js 介绍 二. 常用方法 1. 创建 Decimal 实例 2.加法 add 或 plus 3.减法 sub 或 minus 4.乘法 times 或 mul 5.除法 div 或 dividedBy 6.取模 7.幂运算 8.平方根 9.保留小数位 toFixed方法(四舍五入) 三.项目应用 前端精度丢失问题通常由以下原因…...

Python写UI自动化--playwright(点击操作)
本篇介绍playwright点击操作,click()方法的常用参数 目录 0. selector (必需) 1. modifiers(可选) 2. position(可选) 3. button(可选) 4. click_count(可选) 5. delay 6. timeout(可选) 7. forceTrue(可选) 8. trialTrue(可选) 9. no_wait_after(可选) …...

[C#面对对象] 之抽象方法 虚方法 接口
1.虚方法 我的理解 "法国的“巴黎公社”,俄国的“十月革命”,都是把主要战略方向首先夺取中心城市 " 设计为 一个父类中的虚方法(virtual),这个虚方法已经有实现了(就是通过暴力革命夺取的方法 最终返回 城市)然而秋收暴动(子类)失败…...

docker 发布geoserver服务添加字体
1. 创建容器时可直接挂载到系统字体库 2. 已发布的容器挂载字体目录 关闭docker服务 : systemctl stop docker.socket 修改config.v2.json :位置在 cd /var/lib/docker/containers/容器id 重新启动docker服务:systemctl start docker...

数据赋能(162)——开发:数据整理——技术方法、主要工具
技术方法 从商业角度来看,从前未知的数据分析模式或趋势的发现为企业提供了非常有价值的洞察力。数据整理技术能够为企业对未来的发展具有一定的预见性。数据整理技术可以分成3类:群集、分类和预测。 群集技术: 这是一种将相似的数据项进行…...

安全服务面试
对安全服务是怎么理解的 安全服务对象是人, 渗透测试对象是网站。(我的理解) 安全概念和资讯 安全工具使用 渗透测试 安全基线检查 应急响应 代码审计 安全边界建设 安全规范 1.拿到一个待检测的站,你觉得应该先做什么&…...

昇思25天学习打卡营第23天|LSTM+CRF序列标注
Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|(一)序列标注与条件随机场的关系 Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|(二)CRF模型构建 Mindspore框架CRF条件随机场概率图模型实现文本…...

抖音直播弹幕数据逆向:websocket和JS注入
🔍 思路与步骤详解 🕵️♂️ 思路介绍 首先,我们通过抓包工具进入的直播间,捕获其网络通信数据,重点关注WebSocket连接。发现直播弹幕数据通过WebSocket传输,这种方式比传统的HTTP更适合实时数据的传输。…...

AIGC diffusers文生图模型optimum量化使用案例
参考: https://github.com/huggingface/blog/blob/main/quanto-diffusers.md 安装 pip install optimum-quanto %pip install optimum使用 from optimum.quanto import freeze, qfloat8, quantize from diffusers import PixArtSigmaPipeline import torchpipeline = PixArt…...

PDF怎么转换成Word?这些工具一键搞定!
在日常生活中,我们经常遇到需要将PDF文件转换成Word文档的情况。PDF怎么转换成Word?一些工具的使用十分重要!下文中就为大家推荐几个亲测好用的PDF转换工具。 一、Foxit PDF转换大师(365客户端) 链接:www…...

【TS】TypeScript函数类型:提升函数的类型安全性和可读性
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 TypeScript函数类型:提升函数的类型安全性和可读性1. 引言2. 基本函…...

“八股文”在实际工作中是助力、阻力还是空谈?
前言:在当今快速发展的技术时代,程序员的角色变得日益重要。随着技术的不断进步,招聘流程也在不断演变以适应新的需求。在程序员的招聘过程中,“八股文”作为一种面试现象,已成为不可忽视的一部分。所谓“八股文”&…...

代码随想录算法训练营第22天-leetcode-回溯算法part01:
#回溯算法理论基础 能解决的问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按一定规则全排列&…...

MySql 触发器、存储器练习
一: 触发器 1、建立两个表:goods(商品表)、orders(订单表) 查看数据库:mysql> show databases; 使用数据库:mysql> use mydb16_trigger; 创建goods表: mysql> create table goods(gid char(8) not null primary key, …...

【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧
【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧 本次修炼方法请往下查看 🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地! 🎇 免费获取相关内…...

Mirror学习笔记(一) 简介
文章目录 一、常规学习:Mirror核心功能有服务器和主机 二、时间戳批处理时间戳 三、TCP和UDP四、CCU(同时在线人数)五、SyncDirection(同步方向)六、RTT(往返时间)七、Connection Quality(连接质量)八、Lag Compensati…...

终端pip安装包后,Pycharm却导入失败?新手别慌,3招搞定!
很多小伙伴在学习Python的过程中,都会遇到这种情况:明明在终端用pip安装好了需要的包,但在Pycharm中导入时却报错。难道是安装姿势不对? 例如在cmd中已经有了pandas,但是去pycharm中导入pandas显示没有 先别急着怀疑人生,这很可能是因为pip安装包的路径和Pycharm项目使用…...

Redis 与 Scrapy:无缝集成的分布式爬虫技术
1. 分布式爬虫的概念 分布式爬虫系统通过将任务分配给多个爬虫节点,利用集群的计算能力来提高数据抓取的效率。这种方式不仅可以提高爬取速度,还可以在单个节点发生故障时,通过其他节点继续完成任务,从而提高系统的稳定性和可靠性…...

大厂linux面试题攻略四之Linux网络服务(一)
一、Linux网络服务-SSH服务 1.哪些设置能够提升SSH远程管理的安全等级? ssh的登录验证方式 ssh的登录端口和监听设置: 配置文件: /etc/ssh/sshd_config #Port 22 #ssh服务默认监听端口 #ListenAddress 0.0.0.0 #ssh服务…...

【Pulling fs layer】Linux使用docker-compose的时候,一直Pulling fs layer
当Docker在拉取镜像时卡在“pulling fs layer”阶段,可以通过重启Docker服务来解决。 具体步骤如下: 首先,尝试重启Docker服务。可以通过运行以下命令来重启Docker服务: systemctl restart docker 这个命令会重启Docker服务…...

最新保姆级教程使用WildCard开通Claude3升级ChatGPT4.0(2024.8)
如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布,最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型:Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus,每个模型都展现了卓越的性能与特色。其中&a…...

layui 乱入前端
功能包含 本实例代码为部分傻瓜框架,插入引用layui。因为样式必须保证跟系统一致,所以大部分功能都是自定义的。代码仅供需要用layui框架,但原项目又不是layui搭建的提供解题思路。代码较为通用 自定义分页功能自定义筛选列功能行内编辑下拉、…...