当前位置: 首页 > news >正文

vector模拟实现

目录

vector介绍

vector示意图 

关于vector扩容的问题

vector框架

构造函数

析构函数 

vector有关空间容量函数

insert和erase

pop_back和push_back

其它构造函数

拷贝构造

迭代器区间构造

 运算符重载

关于迭代器失效问题【重点】

有关insert发生迭代器失效

有关erase发生迭代器失效

vector模拟实现整体代码


vector介绍
1. vector 是表示可变大小数组的序列容器,就像数组一样,vector 也采用的连续存储空间来存储元素。也就是意味着可以采用下标对 vector 的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
2. 本质讲, vector 使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector 并不会每次都重新分配大小。
3. vector 分配空间策略: vector 会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
4. 与其它动态序列容器相比( deque, list and forward_list ), vector 在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list forward_list统一的迭代器和引用更好
vector示意图 

上面的图片来自于《STL源码剖析》这本书,从图中我们就可以看出vector实现的基本形式。vector类的成员变量是三个指针:指向起始位置的 _start ,指向最后一个数据的下一个位置的_finish,指向容量的下一个位置的_end_of_storage。 

关于vector扩容的问题

使用以下一段demo进行测试:

void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}

在VS2022下:(大致为1.5倍)

在gcc下:(2倍)

vector框架
template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;
};
构造函数
vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}
析构函数 
~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}
vector有关空间容量函数
size_t capacity() const
{return _endofstorage - _start;
}size_t size() const
{return _finish - _start;
}bool empty()const
{return size() == 0;
}void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];size_t sz = size();if (_start){//memcpy(tmp, _start, sizeof(T) * sz);for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}
}void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}
}

需要注意的是:关于reserve函数,当数值大于当前vector的容量时,需要进行拷贝,使用memcpy进行拷贝时(浅拷贝),对于内置类型不会有问题, 但是对于自定义类型就会有问题,比如string,

会引发扩容,就会引发对旧内容进行拷贝,如果使用memcpy进行拷贝,会出问题:

问题如下:

拷贝完以后,浅拷贝回来对应指针,当旧对象释放后,新对象指向的内容就无意义了,这就是问题所在,解决办法如上面reserve函数。

insert和erase
iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;
}iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;++it;}--_finish;return pos;
}
pop_back和push_back
void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;_finish++;
}void pop_back()
{assert(_finish > _start);_finish--;
}
其它构造函数
拷贝构造
vector(int n, const T& val = T())
{reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}vector(const vector<T>& v)
{reserve(v.capacity());for (auto& e : v){push_back(e);}
}
迭代器区间构造
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
 运算符重载
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}vector<T>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}
关于迭代器失效问题【重点】

vector是一个动态数组,它在内存中连续存储,由于vector在内存中连续存储,因此当进行某些操作时,迭代器可能会失效。

插入元素(可能会导致内存重新分配):在vector中插入元素可能会导致整个容器重新分配内存,这种情况下,所有指向容器中元素的迭代器都会失效。

删除元素:从vector中删除元素会导致被删除元素之后的迭代器失效,这是因为删除操作可能会移动容器中其他元素以填补被删除元素的空位,然而,指向被删除元素之前的迭代器仍然有效。

重新分配:任何导致vector重新分配内存的操作都会使所有迭代器失效。

有关insert发生迭代器失效

迭代器失效原因:

insert时迭代器失效发生在insert前size=capacity,当insert时,会先扩容,但是扩容后,pos还是指向原来的空间的位置,且已经被释放,那么当再次使用pos时,就会导致程序崩溃!!!

解决办法:更新pos位置迭代器,使其指向扩容后对应的位置,并返回pos位置迭代器。

iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

通过查阅文档也可以发现,官方的做法也是返回Insert位置的迭代器:

有关erase发生迭代器失效

erase函数错误写法:

void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;begin++;}_finish--;
}

演示代码:

运行结果:

出错原因:

it永远不会等于finish

解决办法:返回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;
}

对应示例

void test()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//删除所有偶数auto it = v.begin();while(it != v.end()){if(*it % 2 == 0){it = v.erase(*it);}else{it++;}}for(auto e : v){cout << e << " ";}
}

 通过查阅文档也可以发现,官方的做法也是返回erase位置的迭代器:

vector模拟实现整体代码
template<class T>
class vector
{
public: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;}vector(){}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){reserve(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);}vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t sz = size();if (_start){//memcpy(tmp, _start, sizeof(T) * sz);for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}void push_back(const T& x){/*if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;*/insert(end(), x);}iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return 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;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}size_t capacity() const{return _endofstorage - _start;}size_t size() const{return _finish - _start;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;
};

相关文章:

vector模拟实现

目录 vector介绍 vector示意图 关于vector扩容的问题 vector框架 构造函数 析构函数 vector有关空间容量函数 insert和erase pop_back和push_back 其它构造函数 拷贝构造 迭代器区间构造 运算符重载 关于迭代器失效问题【重点】 有关insert发生迭代器失效 有关…...

RV32F\RV32D指令集

RV32F\RV32D指令集 F扩展1、浮点控制状态寄存器2、指令类型F扩展 F扩展增加了32个浮点寄存器f0-f31,每个32位宽,以及一个浮点控制和状态寄存器fcsr,其中包含浮点单元的工作模式和异常状态。FLEN=32表示F单精度浮点扩展,大多数浮点指令对浮点寄存器中的值进行操作。浮点加载…...

安卓VirtualDisplay虚拟屏幕如何实现没有内容显示mirror内容(aosp14版本)

背景&#xff1a; 上一篇blog已经对mirror模式显示镜像屏幕内容进行了详细讲解&#xff1a; 安卓VirtualDisplay虚拟屏幕如何实现没有内容显示mirror屏幕内容 不过这个分析版本是基于aosp13&#xff0c;在这个发布后&#xff0c;有学员在aosp14上进行验证&#xff0c;发现还…...

YOLOv10在RK3588上的测试(进行中...)

1.代码源 国内镜像站在gitcode。这个镜像站也基本上包含了github上常用项目的镜像。然后它的主发布源在这里&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 yolov10是清华主导做的... 然后&#xff0c;在维护列表里看到了这个&#xff1a; 2024年05月31日&am…...

git的ssh安装,windows通过rsa生成密钥认证问题解决

1 windows下载 官网下载可能出现下载太慢的情况&#xff0c;Git官网下载地址为&#xff1a;官网&#xff0c;推荐官网下载&#xff0c;如无法下载&#xff0c;可移步至CSDN&#xff0c;csdn下载地址&#xff1a;https://download.csdn.net/download/m0_46309087/12428308 2 Gi…...

果园预售系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;果树管理&#xff0c;果园管理&#xff0c;果园预约管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;公告&a…...

学了这篇面试经,轻松收割网络安全的offer

网络安全面试库 吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;Java代码审计、web安全、应急响应等&#xff0c;还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 0x1 应届生面试指南 网络安全面…...

双向转发检测BFD(学习笔记)

定义 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种全网统一的检测机制&#xff0c;用于快速检测、监控网络中链路或者IP路由的转发连通状况 BFD检测机制 BFD的检测机制是两个系统建立BFD会话&#xff0c;并沿它们之间的路径周期性发送B…...

Spring Boot:Java 应用开发高效之道

Spring Boot 是一种革命性的框架&#xff0c;旨在简化 Java 应用的创建和部署过程。通过自动化配置和简化项目搭建流程&#xff0c;Spring Boot 大大加速了开发周期&#xff0c;让 Java 应用开发变得更加高效和便捷。 核心优势&#xff1a; 快速启动和简化配置&#xff1a;Spr…...

WebSocket 入门教程

什么是 WebSocket&#xff1f; WebSocket 是一种通信协议&#xff0c;它在单个 TCP 连接上提供全双工通信。与传统的 HTTP 不同&#xff0c;WebSocket 允许服务器主动向客户端推送数据&#xff0c;而不仅仅是客户端请求数据。这使得 WebSocket 非常适用于需要低延迟和实时通信…...

C++中extern “C“的用法

目的 extern "C"是经常用到的东西&#xff0c;面试题目也经常出现&#xff0c;然则&#xff0c;实际用时&#xff0c;还是经常遗忘&#xff0c;因此&#xff0c;深入的了解一下&#xff0c;以增强记忆。 extern "C"指令非常有用&#xff0c;因为C和C的近亲…...

常见机器学习的原理及优略势

有监督 一、线性回归&#xff08;Linear Regression) 1. 算法原理 线性回归&#xff08;Linear Regression&#xff09;是一种基本的回归算法&#xff0c;它通过拟合一个线性模型来预测连续型目标变量。线性回归模型的基本形式是&#xff1a;y w1 * x1 w2 * x2 … wn * …...

李诞-2021.8脱口秀工作手册-1-工作的本质是交易;脱口秀是一份和生活分不开的工作,你的全部人生都理应要为你的创作提供养分,为它服务。

1 首先&#xff0c;这是一份工作&#xff0c;工作的本质是交易。 我们在用自己的时间和才能&#xff0c;通过一家公司&#xff0c;与市场交换金钱。 根据诺贝尔经济学奖得主科斯的著名理论&#xff0c;公司会产生的原因&#xff0c;就是人们自己直接与市场交易成本太高&…...

生命在于学习——Python人工智能原理(3.3)

三、深度学习 4、激活函数 激活函数的主要作用是对神经元获得的输入进行非线性变换&#xff0c;以此反映神经元的非线性特性。常见的激活函数有线性激活函数、符号激活函数、Sigmod激活函数、双曲正切激活函数、高斯激活函数、ReLU激活函数。 &#xff08;1&#xff09;线性…...

【C++11】智能指针问题

文章目录 RAII一、auto_ptr二、unique_ptr三、shared_ptrshared_ptr的循环引用问题 四、weak_ptr总结 RAII RAII就是将资源交给一个对象管理&#xff0c;这个对象能进行正常的管理和释放资源。 一、auto_ptr auto_ptr的问题是&#xff1a;在拷贝构造和赋值重载时&#xff0c…...

借助ChatGPT撰写学术论文,如何设定有效的角色提示词指

大家好&#xff0c;感谢关注。这个给大家提供关于论文写作方面专业的讲解&#xff0c;以及借助ChatGPT等AI工具如何有效辅助的攻略技巧。有兴趣的朋友可以添加我&#xff08;yida985&#xff09;交流学术写作或ChatGPT等AI领域相关问题&#xff0c;多多交流&#xff0c;相互成就…...

成功在服务器liunx-ubantu上安装pytorch

sudo pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118参考链接&#xff1a; 教程&#xff08;部分参考&#xff09; Pytorch官网...

【面试干货】抽象类和接口的区别

【面试干货】抽象类和接口的区别 1、抽象类1.1、什么是抽象类&#xff1f;1.2、示例代码 2、接口2.1、什么是接口&#xff1f;2.2、示例代码 3、比较和总结3.1、使用场景3.2、关键区别3.3、代码示例比较 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&am…...

python爬虫:实现动态网页的爬取,以爬取视频为例

引言: 爬虫也被称为网络蜘蛛(Spider),是一种自动化的软件程序,能够在互联网上漫游,按照一定的规则和算法抓取数据。 爬虫技术广泛应用于搜索引擎、 数据挖掘 、信息提取等领域,是互联网技术的重要组成部分。 摘要: 作为爬虫的初学者,网页越简单越好,因为网页的结构…...

Incredibuild for Mac 来了!

Mac 开发者在寻找适合自己需求的工具时可能会遇到一些困难&#xff0c;因为 Mac 操作系统相对封闭&#xff0c;不像其他系统那样开放和灵活。尽管如此&#xff0c;Mac 开发者在开发应用程序时的需求&#xff08;比如功能、效率等&#xff09;和使用其他操作系统的开发者是类似的…...

递归解析 LXML 树并避免重复进入某个节点

1、问题背景 我们在使用 LXML 库解析 MathML 表达式时&#xff0c;可能会遇到这样一个问题&#xff1a;在递归解析过程中&#xff0c;我们可能会重复进入同一个节点&#xff0c;导致解析结果不正确。例如&#xff0c;我们希望将以下 MathML 表达式解析为 Python 表达式&#x…...

GaussDB技术解读——GaussDB架构介绍(三)

目录 9 智能关键技术方案 智能关键技术一&#xff1a;自治运维系统 智能关键技术二&#xff1a;库内AI引擎 智能关键技术三&#xff1a;智能优化器 10 驱动接口关键技术方案 GaussDB架构介绍&#xff08;二&#xff09;从数据持久化存取层(DataNode)关键技术方案、全局事…...

解锁ChatGPT:从原理探索到GPT-2的中文实践及性能优化

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三连支…...

【WPF】中的ListBox的ScrollIntoView方法使用

在WPF中&#xff0c;ListBox控件的ScrollIntoView方法用于确保指定的项在可视区域内可见。如果该项不在当前的视图中&#xff0c;该方法会滚动列表&#xff0c;使该项出现在视图中。这对于在用户交互或程序逻辑中需要突出显示特定列表项的场景非常有用。但是不会指定滚动的对齐…...

信息安全等级保护测评(等保测评)定级的重要性与实施路径

#等保测评##黑龙江等保测评##哈尔滨等保测评# 在数字化转型的浪潮中&#xff0c;信息安全已成为保障国家安全、社会稳定及企业发展的基石。信息安全等级保护测评&#xff08;简称“等保测评”&#xff09;&#xff0c;作为中国网络安全领域的基础性制度&#xff0c;为组织机构的…...

Python库

Python库 babel huey 图片视频处理 moviepy 一个用于视频编辑的Python模块,可用于进行视频的基本操作(如剪切、连接、标题插入)、视频合成(也称非线性编辑)、视频处理或创建高级效果 patchworklib 一个专注于图像拼接和合成的Python库 patchworklib 一个专注与图…...

pytest+requests+allure自动化测试接入Jenkins学习

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 最近在这整理知识&#xff0c;发现在pytest的知识文档缺少系统性&#xff0c;这里整理一下&…...

你能不能手敲出Spring框架?

Spring最成功的地方在于创始人Rod Johnson提出的IOC、AOP核心理念&#xff0c;反而不是其本身的技术。技术上今天可以有Spring春天&#xff0c;明天就可以有Autumn秋天。 核心理念有多重要&#xff1f;就如1871年巴黎公社的失败。公社在对抗法国zf和普鲁士占领军的背景下成立&…...

实体店如何通过私域获取流量?

随着互联网的快速发展和消费者购物习惯的变化&#xff0c;私域流量对于实体店的重要性日益凸显。私域流量是指企业在自己的平台上沉淀的、可以免费使用、多次利用的流量&#xff0c;如微信生态下的朋友圈、公众号、企业微信等。对于实体店而言&#xff0c;有效利用私域流量不仅…...

互联网与人工智能时代:问题的新形态与解答的挑战

随着互联网的普及和人工智能技术的飞速发展&#xff0c;我们仿佛进入了一个答案触手可及的新时代。然而&#xff0c;就在我们以为问题将因此逐渐减少之时&#xff0c;实则问题的形态和内涵正在发生深刻的变化。因此&#xff0c;我们不应简单地将互联网和人工智能视为解决问题的…...