【C++ STL】手撕vector,深入理解vector的底层

vector的模拟实现

  • 前言
  • 一.默认成员函数
    • 1.1常用的构造函数
      • 1.1.1默认构造函数
      • 1.1.2 n个 val值的构造函数
      • 1.1.3 迭代器区间构造
      • 1.1.4 initializer_list 的构造
    • 1.2析构函数
    • 1.3拷贝构造函数
    • 1.4赋值运算符重载
  • 二.元素的插入,删除,查找操作
    • 2.1 operator[]重载函数
    • 2.2 push_back函数:尾插一个元素
    • 2.3 pop_back函数:尾删一个元素
    • 2.4 insert函数:指定位置插入元素
    • 2.5 erase:删除指定位置的元素
  • 三.front和back函数以及迭代器的实现
    • 3.1 front函数: 获取第一个元素
    • 3.2 back函数: 获取最后一个元素
    • 3.3 begin和end函数
    • 3.4 swap函数

前言

vector是一个类模板,它本质是一个顺序表,通过我们之前的学习,我们一般会这样来定义一个顺序表:

template<class T>
class vector
{T* _a;size_t _size;size_t _capacity;
};

这种定义方式当然是可以的,但是我们通过看P.J.版本的stl的源码会发现,其中对vector的定义大概是这样的:

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;//当前容量的下一个位置};

他是通过三个指针来维护这个顺序表的,我们这篇博客也是采用这种定义方式来实现一个简易版vector的.

一.默认成员函数

1.1常用的构造函数

1.1.1默认构造函数

默认构造是实现一个空的vector,不分配任何内存
代码实现:

vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}
size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}

测试用例:

int main()
{vector<int> v;cout << "size:" << v.size() << endl;cout << "capacity:" << v.capacity() << endl;return 0;
}

输出结果:

size:0
capacity:0

1.1.2 n个 val值的构造函数

初始化一个vector,其中用n个val值得对象来填充.
代码示例:

		void reserve(size_t n)  {if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];if (_start){//不可以这样写,因为如果vector中存的类型是自定义类型,存在浅拷贝的问题//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < oldsize; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}}void push_back(const T& x){if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}vector(size_t n, const T& val = T()){//考虑到扩容带来的效率低下问题,我们可以提前开好足够大的空间reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}

测试用例:

int main()
{vector<int> v2(10 , 1);cout << "size:" << v2.size() << endl;cout << "capacity:" << v2.capacity() << endl;return 0;
}

输出结果:

size:10
capacity:10

1.1.3 迭代器区间构造

代码实现:

		template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}

测试用例:

int main()
{string s1 = "aaaaaaa";vector<char> v3(s1.begin(), s1.end());return 0;
}

输出结果:
在这里插入图片描述

1.1.4 initializer_list 的构造

用一个初始化列表来构造
代码实现:

		vector(initializer_list<T> il){reserve(il.size());for (const auto& e : il){push_back(e);}}

测试用例:

int main()
{vector<int> v4{ 1,2,3,4,5,6,7,8,9 };return 0;
}

输出结果:
在这里插入图片描述

1.2析构函数

析构函数:完成对象中的资源的回收清理,防止出现内存泄露.

代码实现:

		~vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}

1.3拷贝构造函数

代码实现:

		vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}

测试用例:

int main()
{	vector<int> v4{ 1,2,3,4,5,6,7,8,9 };vector<int> v5 = v4;return 0;
}

输出结果:
在这里插入图片描述

1.4赋值运算符重载

代码实现:

		void swap(vector<int>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//依旧是熟悉的现代写法vector<T>& operator=(vector<T> v){swap(v);return *this;}

这里复用的是拷贝构造,拷贝构造我们已经测试过了没有什么问题,这里应该也是正常的,这里就不测试了.

二.元素的插入,删除,查找操作

2.1 operator[]重载函数

这里我们需要重载两个版本,一个是普通对象调用,另一个是const对象调用.
代码实现:

		T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}

测试用例:

int main()
{vector<int> v1{ 1,2,3,4,5,6,7,8,9 };for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;return 0;
}

输出结果:

1 2 3 4 5 6 7 8 9

2.2 push_back函数:尾插一个元素

代码实现:

		void push_back(const T& x){if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}

测试用例:

int main()
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;return 0;
}

输出结果:

1 2 3 4 5

2.3 pop_back函数:尾删一个元素

实现思路:
1.将_finish指针向前移动一位,即删除最后一个元素。
2.当size已经为0,即vector中已经没有数据时,就不再删除.
代码实现:

		void pop_back(){assert(size() > 0);--_finish;}

测试用例:

int main()
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.pop_back();for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;return 0;
}

输出结果:

1 2 3 4 5
1 2 3 4

2.4 insert函数:指定位置插入元素

这里需要注意迭代器失效的问题,如果不了解什么是迭代器失效的小伙伴,可以去:vector ,里面有迭代器失效场景的详细介绍.
代码实现:

iterator insert(iterator pos, const T& x)
{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;
}

测试用例:

int main()
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.insert(v1.begin() + 2, 1000);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;return 0;
}

输出结果:

1 2 3 4 5
1 2 1000 3 4 5

2.5 erase:删除指定位置的元素

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;}

测试用例:

int main()
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.erase(v1.begin() + 2);for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;}return 0;
}

输出结果:

1 2 3 4 5
1 2 4 5

三.front和back函数以及迭代器的实现

3.1 front函数: 获取第一个元素

代码实现:

		T& front(){assert(size() > 0);return _start[0];}const T& front() const{assert(size() > 0);return _start[0];}

3.2 back函数: 获取最后一个元素

代码实现:

		T& front(){assert(size() > 0);return _start[0];}const T& front() const{assert(size() > 0);return _start[0];}

3.3 begin和end函数

代码实现:

		iterator begin(){return _start;}const_iterator begin() const{return _start;}iterator end(){return _finish;}const_iterator end() const{return _finish;}

3.4 swap函数

代码实现:

		void swap(vector<int>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}

希望对大家有所帮助,感谢观看!

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

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

相关文章

[已解决] Install PyTorch 报错 —— OpenOccupancy 配环境

目录 关于 常见的初始化报错 环境推荐 torch, torchvision & torchaudio cudatoolkit 本地pip安装方法 关于 OpenOccupancy: 语义占用感知对于自动驾驶至关重要&#xff0c;因为自动驾驶汽车需要对3D城市结构进行细粒度感知。然而&#xff0c;现有的相关基准在城市场…

TriLite完成A轮扩展融资:加速AR微型投影仪技术创新与市场拓展

近日,全球领先的AR微型投影仪开发商TriLite宣布成功完成A轮扩展融资,将A轮融资总额提升至超过2000万欧元。这一轮融资不仅彰显了资本市场对TriLite技术实力和市场潜力的高度认可,更为其后续在AR微型投影仪领域的技术研发、产品迭代以及市场拓展提供了坚实的资金保障。以下是…

大厂笔试现已经禁用本地IDE怎么看

如果我说本来面试做题这种事情就是反人类你相信吗&#xff1f; 这个罪恶的源头就是 Google&#xff0c;说是为了选择高素质的计算机编程水平的人才&#xff0c;然后把面试就变成了考试&#xff0c;最大的受益者当然是印度人了。 当把一个考察过程变成标准化的考试过程&#x…

【AI知识点】置信区间(Confidence Interval)

置信区间&#xff08;Confidence Interval, CI&#xff09; 是统计学中用于估计总体参数的范围。它给出了一个区间&#xff0c;并且这个区间包含总体参数的概率等于某个指定的置信水平&#xff08;通常是 90%、95% 或 99%&#xff09;。与点估计不同&#xff0c;置信区间通过区…

Unity Input System自动生成配置

参考视频 创建及配置新输入系统 New Input System&#xff5c;Unity2022.2 最新教程《勇士传说》入门到进阶&#xff5c;4K_哔哩哔哩_bilibili ProjectSettings设置 Unity编辑器菜单栏选择Edit->Project Settings->Player->Other Settings,将Api Compatibility Level…

OpenAI 开发者大会!实时语音功能有API了,GPT-4o支持多模态微调,上下文cache功能上线

家人们&#xff01;十一假期第1天&#xff0c; OpenAI一年一度的开发者大会又来了惹&#xff01;今年的开发者大会分成三部分分别在美国、英国、新加坡三个地点举办&#xff0c;刚刚结束的是第一场。 去年的OpenAI开发者大会公布了GPT-4 Turbo和GPTs&#xff0c;今年没有大更新…

allegro精确画圆形边框

1.显示原点位置&#xff1a; 2.class-subclass依次选择Board Geometry-Outline 3.菜单ADD---Circle,右侧option,依次设置如下&#xff0c;如图可设置为圆心&#xff08;0&#xff0c;0&#xff09;&#xff0c;半径为42mm的边框&#xff0c;不要忘了右键Done&#xff0c;完成绘…

【目标检测】工程机械车辆数据集2690张4类VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2694 标注数量(xml文件个数)&#xff1a;2694 标注数量(txt文件个数)&#xff1a;2694 标注…

《Windows PE》3.2.4节表

节表由多个节表项&#xff08;IMAGE_SECTION_ HEADER&#xff09;组成&#xff0c;每个节表项&#xff08;40个字节&#xff09;记录了 PE中与某个特定的节有关的信息&#xff0c;如节的属性、节 的大小、在文件和内存中的起始位置等。节表中节的数量由字段IMAGE_FILE_HEADER. …

防止错误输入!Excel单元格限制输入内容的三种有效方式

在Excel中&#xff0c;限制单元格输入内容可以帮助避免数据输入错误&#xff0c;确保数据的一致性和准确性。今天小编分享三种方法&#xff0c;可以轻松限制Excel单元格的输入内容&#xff0c;确保数据输入符合预期要求&#xff0c;一起来看看吧&#xff01; 方法一&#xff1a…

el-pagination组件封装

组件使用 源代码&#xff1a; <script setup> import Pagination from /components/pagination/index.vue import {ref} from "vue";const pageNum ref(1) const pageSize ref(10) const total ref(120)function loadData() {// 加载数据 } </script>…

[云] Hands-on with a sample application--DockerCoins 挖矿程序!

DockerCoins 挖矿程序&#xff01;&#x1f4b0;&#x1f433;&#x1f4e6;&#x1f6a2; 不&#xff0c;你不能用 DockerCoins 买咖啡。 DockerCoins 如何工作&#xff1a; 生成一些随机字节&#xff1a; 程序首先生成一串随机的字节数据。这些随机字节用于模拟挖矿过程中的…

R语言绘制散点图

散点图是一种在直角坐标系中用数据点直观呈现两个变量之间关系、可检测异常值并探索数据分布的可视化图表。它是一种常用的数据可视化工具&#xff0c;我们通过不同的参数调整和包的使用&#xff0c;可以创建出满足各种需求的散点图。 常用绘制散点图的函数有plot()函数和ggpl…

算法专题三: 二分查找

目录 1. 朴素版: 二分查找2. 查找排序数组元素第一个和最后一个位置3. 搜索插入位置4. x的平方根5. 山脉数组的峰顶索引6. 寻找旋转数组中的最小值7. 点名 博客主页: 酷酷学!!! 感谢您的关注~ 正文开始 1. 朴素版: 二分查找 题目思路: 仅需根据题意, 找出二段性, 正确更新下标…

躺平成长:微信小程序运营日记第二天

在进行属于生活的开源之后&#xff0c;自己更加感受到自己存在的渺茫&#xff0c;同时更加开始深刻领会&#xff0c;开源的重要性&#xff0c;在开源&#xff0c;开放&#xff0c;创造&#xff0c;再创新的思维模式下&#xff0c;不发布八部金刚功相关的训练视频&#xff0c;自…

课设实验-数据结构-线性表-手机销售

题目&#xff1a; 代码&#xff1a; #include<stdio.h> #include<string.h> #define MaxSize 10 //定义顺序表最大长度 //定义手机结构体类型 typedef struct {char PMod[10];//手机型号int PPri;//价格int PNum;//库存量 }PhoType; //手机类型 //记录手机的顺序…

如何快速切换电脑的ip地址

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;其重要性日益凸显。无论是出于保护个人隐私的需要&#xff0c;还是为了访问特定的网络服务等&#xff0c;快速切换电脑的IP地址已成为许多用户的迫切需求。本文将为你介绍几种实用的方法&#xff0c;帮…

草莓成熟度检测数据集 3700张 草莓成熟 带标注voc yolo 3类

草莓成熟度检测数据集 3700张 草莓成熟 带标注voc yolo 草莓成熟度检测数据集 名称 草莓成熟度检测数据集 (Strawberry Maturity Detection Dataset) 规模 图像数量&#xff1a;共3713张图像。类别&#xff1a;分为三个级别&#xff1a;未熟 (raw)、半熟 (turning) 和 成熟…

01_SQLite

文章目录 ** SQLite 存储各类和数据类型 **** SQLite 五种亲缘类型** SQLite 创建数据表删除数据表插入数据信息从数据表中获取数据&#xff0c;以结果表的形式返回数据&#xff08;结果集&#xff09;updatedistinctorder bygroup byhaving触发器删除一个触发器&#xff08;tr…

软件设计师——数据结构

本博文所有内容来自于B站up主zst_2001 目录 时间复杂度 常规数据结构 链表 栈与队列 ​编辑 串 数组 树 卡特兰数&#xff1a; 平衡二叉树 哈夫曼 图 AOV 排序 顺序 折半 哈希 时间复杂度 常规数据结构 链表 栈与队列 串 找i位置前面的字符串&#xff0c…