【STL】vector的模拟实现

目录

vector的介绍和使用

vector的介绍

vector的使用

构造函数

 迭代器

空间增长问题 

vector的增删查改等 

vector的迭代器失效问题 

vector的模拟实现

insert

reserve

push_back

push_front

resize

erase

pop_back

pop_front

代码


vector的介绍和使用

vector的介绍

1)vector是可变数组大小的序列容器

2)与数组相比,vector提供了更多的功能

3)vector会分配一些额外的空间以适应可能得增长

vector的使用

构造函数

构造函数说明
vector()无参构造
vector(size_type n, const value_type& val = value_type ())构造并初始化n个val
vector(const vector& x);拷贝构造
vector(InputIterator first, InputIterator last);使用迭代器进行初始化构造

代码演示:

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v1; //无参构造vector<int> v2(3, 9); //构造并初始化n个val(9 9 9)vector<int>::iterator it2 = v2.begin();while (it2 != v2.end()){cout << *it2 << " ";++it2;}cout << endl;vector<int> v3 = v2; //拷贝构造(9 9 9)vector<int>::iterator it3 = v3.begin();while (it3 != v3.end()){cout << *it3 << " ";++it3;}cout << endl;vector<int> v4(v3.begin(), v3.end()); //迭代器区间构造(9 9 9)vector<int>::iterator it4 = v4.begin();while (it4 != v4.end()){cout << *it4 << " ";++it4;}cout << endl;return 0;
}

 迭代器

函数说明
begin()获取第一个元素的迭代器
end()获取最后一个元素的下一个位置的迭代器
rbegin()获取最后一个元素的迭代器
rend()获取第一个元素前一个位置的迭代器

 代码演示:

#include <iostream>
#include <vector>
using namespace std;int main()
{int arr[] = { 1,2,3,4,5 };vector<int> v(arr, arr + 5); //5 4 3 2 1 vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";++rit;}cout << endl;return 0;
}

空间增长问题 

函数说明
size()获取数据个数
empty()判断是否为空
capacity()获取容量大小
resize()改变vector的size
reserve()改变vector的capacity

1)在vs下,capacity是按1.5倍增长,g++下是按2倍增长

2)reserve只负责开辟空间,如果知道开多大的空间,reserve还可以减少扩容的代价

3)resize在开辟空间的同时还会进行初始化,影响size

测试vector在VS2019下的默认扩容机制:


#include <iostream>
#include <vector>
using namespace std;void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();for (int i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed:" << sz << endl;}}
}int main()
{TestVectorExpand();return 0;
}

reserve的使用:


#include <iostream>
#include <vector>
using namespace std;void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();v.reserve(100);for (int i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed:" << sz << endl;}}
}int main()
{TestVectorExpand();return 0;
}

如果已知大概需要的空间,可以提前开好,避免频繁扩容导致效率低

vector的增删查改等 

函数说明
push_back尾插
pop_back尾删
find查找(这个是算法模块实现的,并非vector的成员函数)
insert在指定位置前插入
erase删除指定位置的数据
swap交换两个vector的数据空间
operator[ ]像数组一样访问

以上这些函数的使用相对比较简单,这里就不赘述了,直接进入重点vector的迭代器失效问题。

vector的迭代器失效问题 

vector迭代器的底层其实就是个原生指针T*,之所以会导致迭代器失效,是因为vector的底层空间发生了改变,导致T*为野指针。总结起来就是一句话:任何引起vector底层改变的操作都可能会导致vector迭代器失效。比如:insert、erase、resize、reverse等。

以erase为例:


#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v{ 1,2,3,4 };//找到指定删除的位置vector<int>::iterator pos = find(v.begin(), v.end(), 3);//删除指定位置的值v.erase(pos);cout << *pos << endl;return 0;
}

删除pos位置的元素后,pos位置之后的元素会往前挪动,没有导致底层空间的变化,理论上讲迭代器应该是还没失效的。但是,我们可以想象如果删除的pos是最后一个元素,那么pos刚好是end的位置,然而end是没有存放有效数据的,这时pos就失效了。因此,删除任意位置的元素,VS都认为该位置的迭代器失效了。 

迭代器失效的解决方法:在使用前,对迭代器重新赋值即可。

示例:

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v{ 1,2,3,4 };//找到指定删除的位置vector<int>::iterator pos = find(v.begin(), v.end(), 3);//删除指定位置的值pos = v.erase(pos);cout << *pos << endl;return 0;
}

vector的模拟实现

vector的基本框架:

namespace pcz
{template <class T>class vector{typedef T* iterator;//vector的迭代器为原生指针typedef const T* const_iterator;//const修饰的是T指向的内容~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}//……protected:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
}

end_of_storage是啥?

end_of_storage是指向vector容器当前分配的内存空间的末尾的指针或迭代器,是容量的边界

insert

在指定位置进行插入,挪动数据必不可少。


iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t old_len = pos - _start;//扩容导致迭代器失效,需要计算偏移量size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + old_len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;return pos;
}

reserve

负责开辟空间。

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();//记录size,看看size的计算方法就明白了T* tmp = new T[n];if (_start){//直接用memcpy会有问题,浅拷贝//memcpy(tmp, _start, sizeof(T) * size());//_start = tmp;for (int i = 0; i < size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}

push_back

复用insert。

iterator push_back(const T& x)
{return insert(end(), x);
}

push_front

复用insert。

iterator push_front(const T& x)
{return insert(begin(), x);
}

resize

再谈resize的作用:

1)resize用于调整vector的大小,它可以增加或减少vector中元素的数量,以达到指定的新大小。

2)如果vector的新大小大于当前大小,则会在末尾添加足够数量的新元素。、

3)如果vector的新大小小于当前大小,则vector中超出新大小的部分会被丢弃,vector的大小变为指定的新大小。

void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;return;}if(n > capacity())reserve(n);iterator it = _finish;_finish = _start + n;while (it != _finish){*it = val;++it;}
}

erase

删除指定位置的元素,伴随着数据挪动。

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

iterator pop_back()
{return erase(_finish - 1);
}

pop_front

iterator pop_front()
{return erase(_start);
}

代码


template <class T>
class vector
{
public:
typedef T* iterator;//vector的迭代器为原生指针
typedef const T* const_iterator;//const修饰的是T指向的内容~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}iterator begin()
{return _start;
}const_iterator cbegin() const
{return _start;
}iterator end()
{return _finish;
}const_iterator cend() const
{return _finish;
}void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();//记录size,看看size的计算方法就明白了T* tmp = new T[n];if (_start){//直接用memcpy会有问题,浅拷贝//memcpy(tmp, _start, sizeof(T) * size());//_start = tmp;for (int i = 0; i < size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;return;}if(n > capacity())reserve(n);iterator it = _finish;_finish = _start + n;while (it != _finish){*it = val;++it;}
}iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t old_len = pos - _start;//扩容导致迭代器失效,需要计算偏移量size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + old_len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;++_finish;return pos;
}iterator push_back(const T& x){return insert(end(), x);}iterator push_front(const T& x){return insert(begin(), x);}iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;
}iterator pop_back()
{return erase(_finish - 1);
}iterator pop_front()
{return erase(_start);
}bool empty() const
{return _start == nullptr;
}protected:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};

完~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据的统计探针:SKlearn中的统计分析方法

数据的统计探针&#xff1a;SKlearn中的统计分析方法 在数据科学领域&#xff0c;统计分析是理解和解释数据的关键工具。Scikit-learn&#xff08;简称sklearn&#xff09;&#xff0c;作为Python中一个功能强大的机器学习库&#xff0c;提供了多种方法来进行数据的统计分析。…

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

每日一道算法题 LCR 150. 彩灯装饰记录 II

题目 LCR 150. 彩灯装饰记录 II - 力扣&#xff08;LeetCode&#xff09; Python # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.righ…

中英双语介绍超跑:Ferrari,Lamborghini,Porsche,Bentley,McLaren

中文版 法拉利&#xff08;Ferrari&#xff09; 历史 法拉利成立于1939年&#xff0c;由恩佐法拉利&#xff08;Enzo Ferrari&#xff09;创立&#xff0c;总部位于意大利马拉内罗。法拉利以其豪华跑车和赛车而闻名&#xff0c;特别是在一级方程式赛车&#xff08;F1&#x…

2024年7月8日-7月14日(ue5肉鸽视频p34-p44)

试过重点放在独立游戏上&#xff0c;有个indienova独立游戏团队是全职的&#xff0c;由于他们干了几个月&#xff0c;节奏暂时跟不上&#xff0c;紧张焦虑了。五一时也有点自暴自弃了&#xff0c;实在没必要&#xff0c;按照自己的节奏走即可。精力和时间也有限&#xff0c;放在…

个人视角,社会影响力:自媒体的魅力所在

随着数字化时代的到来&#xff0c;自媒体正成为信息传播领域的一场革命。个人视角与社会影响力的结合&#xff0c;赋予了自媒体独特的魅力。在传统媒体受限制的同时&#xff0c;自媒体为每个人提供了表达自己观点和思想的自由。个体的真实视角使得自媒体在信息传播中发挥着重要…

14-40 剑和诗人14 - 为什么机器学习需要合成数据

​​​​​​ 数据是人工智能的命脉。如果没有高质量、有代表性的训练数据&#xff0c;我们的机器学习模型将毫无用处。但随着神经网络规模越来越大、人工智能项目越来越雄心勃勃&#xff0c;人们对数据的需求也越来越大&#xff0c;我们面临着一场危机——现实世界的数据收集和…

如何优化 PostgreSQL 中对于复杂数学计算的查询?

文章目录 一、理解复杂数学计算的特点二、优化原则&#xff08;一&#xff09;索引优化&#xff08;二&#xff09;查询重写&#xff08;三&#xff09;数据库配置调整&#xff08;四&#xff09;使用数据库内置函数的优势 三、具体的优化方案和示例&#xff08;一&#xff09;…

华为开源自研AI框架昇思MindSpore应用案例:FCN图像语义分割

Mask R-CNN MaskRCNN是一种概念简单、灵活、通用的目标实例分割框架&#xff0c;在检测出图像中目标的同时&#xff0c;还为每一个实例生成高质量掩码。这种称为Mask R-CNN的方法&#xff0c;通过添加与现有边框检测分支平行的预测目标掩码分支&#xff0c;达到扩展Faster R-CN…

LRU 缓存机制

题目 运用你所掌握的数据结构&#xff0c;设计和实现一个 LRU (最近最少使用) 缓存机制 。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&a…

leetcode判断二分图

判断二分图 图的问题肯定要用到深度优先遍历或者广度优先遍历&#xff0c;但又不是单纯的深度优先遍历算法和广度优先遍历算法&#xff0c;而是需要在遍历的过程中加入与解决题目相关的逻辑。 题干中说了&#xff0c;这个图可能不是连通图&#xff0c;这个提示有什么作用呢&a…

shared_ptr 线程安全

为什么 shared_ptr 可以安全地在多个线程中共享&#xff1f; 循环引用 因为shared_ptr std::shared_ptr 的引用计数是线程安全的。这意味着你可以在多个线程中安全地拷贝、赋值和销毁 std::shared_ptr。然而&#xff0c;访问或修改 shared_ptr 所指向的对象时&#xff0c;需要…

昇思25天学习打卡营第20天|LSTM+CRF序列标注

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标…

明日周刊-第15期

赶在周末结束前输出一把&#xff0c;周日的晚上大家要睡个好觉哦。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 科技创新与基础设施建设 深中通道正式通车试运营 时间&#xff1a;6月30日 内容&#xff1a;国家重大工程深中通道正式通车试运营&#xff0c;标志着珠江口东…

深入理解Java可执行JAR文件

目录 引言JAR文件简介创建JAR文件 使用JDK的jar工具使用IDE创建JAR文件 指定Main-Class属性 在MANIFEST.MF文件中指定使用jar工具指定 运行可执行JAR文件在Maven项目中创建可执行JAR文件 配置pom.xml使用maven-jar-plugin 在Gradle项目中创建可执行JAR文件 配置build.gradle使…

MySQL中in和exists的区别

in和exists都是在 SQL 中用于检查子查询中是否存在数据的谓词&#xff0c;它们的区别主要体现在语法、用途、效率、错误处理以及子查询范围等方面&#xff0c;具体区别如下&#xff1a; 语法&#xff1a; exists&#xff1a;exists (子查询)in&#xff1a;列 in (子查询) 或 子…

Java实现布隆过滤器的几种方式

布隆过滤器应用场景: 为预防大量黑客故意发起非法的时间查询请求,造成缓存击穿,建议采用布隆过滤器的方法解决。布隆过滤器通过一个很长的二进制向量和一系列随机映射函数(哈希函数)来记录与识别某个数据是否在一个集合中。如果数据不在集合中,能被识别出来,不需要到数…

吉时利KEITHLEY KI-488驱动和说明

吉时利KEITHLEY KI-488驱动和说明

[吃瓜教程]南瓜书第6章支持向量机

0.补充知识 0.1 超平面 定义&#xff1a; 超平面是指在&#x1d45b;维空间中&#xff0c;维度为 &#x1d45b;−1的子空间。它是分割空间的一个平面。 性质&#xff1a; n维空间的超平面 ( w T x b 0 , 其中 w , x ∈ R n ) (w^Tx_b0,其中w,x\in \mathbb R^n) (wTxb​0,其…

【大模型】大语言模型:光鲜背后的阴影——事实准确性和推理能力的挑战

大语言模型&#xff1a;光鲜背后的阴影——事实准确性和推理能力的挑战 引言一、概念界定二、事实准确性的局限2.1 训练数据的偏差2.2 知识的时效性问题2.3 复杂概念的理解与表述 三、推理能力的局限3.1 表层理解与深层逻辑的脱节3.2 缺乏常识推理3.3 无法进行长期记忆和连续推…