【C++】手撕 Vector类

目录

1,vector类框架

2,vector ()

3,pinrt()

4,vector(int n, const T& value = T())

5,vector(const vector& v)

6,vector(InputIterator first, InputIterator last)

7,~vector()

8,iterator begin()

9,iterator end()

10,size() const

11,capacity() const

12,reserve(size_t n)

13,resize(size_t n, const T& value = T())

14,push_back(const T& x)

15,pop_back()

16,insert(iterator pos, const T& x)

17,erase(iterator pos)

18,empty()

19,operator[](size_t pos)

20,operator= (vector v)

21,总结


上面我们认识了 vector 类,有了一个大概的理解,下面我们来实现一下 vector 类的框架,来更好的熟悉 vector 类,也让我们对其有着更深的理解; 

1,vector类框架

我们先写一个 vector 类的基本框架;

namespace newVector
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;private:iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};
}

vector 类里面是可以包含很多类型的,所以我们用模板来表示,以应用各种场景,vector 类里面多用迭代器的方式来表示,vector 的迭代器其实就是一个原生指针;

_start 指向数据块的开始,_finish 指向有效数据的尾,_endOfStorage 指向存储容量的尾;

2,vector ()

vector()
{}

因为我们在构造框架的时候已经给了缺省值,所以可以不用在写了;

可以看到这里已经初始化了;

3,pinrt()

就是打印输出嘛,方便后续测试;

		void pinrt(){for (size_t i = 0; i < size(); i++){cout << _start[i] << " ";}}

4,vector(int n, const T& value = T())

我们都会用,初始化 n 个 value;

		vector(int n, const T& value = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(value);}}

有人不知道缺省值给个 T()是什么意思,首先 T()是匿名对象,默认就是编译器对内置类型的初始化; 

测试一下:

int main()
{newVector::vector<int> v1(5, 8);v1.pinrt();return 0;
}

现在我们不给初始化的值试试:

int main()
{newVector::vector<int> v1(5);v1.pinrt();return 0;
}

 默认初始化为0;

5,vector(const vector<T>& v)

拷贝构造(深拷贝)

		vector(const vector<T>& v){reserve(v.capacity());//memcpy(_start, v._start, sizeof(T)*v.size());for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();}

为什么不用 memcpy 呢,上面那个明显要便捷一点,因为 memcpy 是浅拷贝,如果遇到 T是string类的时候就会因为析构函数多次析构同一块空间而报错,而下面这个挨个赋值是深拷贝,他们两个 _start 不会指向同一块空间;

int main()
{newVector::vector<int> v1(5,8);newVector::vector<int> v2(v1);v2.pinrt();return 0;
}

可以看到也是 OK 的;

6,vector(InputIterator first, InputIterator last)

这个要配合模板使用,这代表一个范围,只要是同类型的都可以;

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

先扩容嘛,像这种区间都是左闭右开的,然后再挨个尾插即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);v1.pinrt();return 0;
}

只要是同类型的都可以,像这种的数组都行;

7,~vector()

析构函数

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

delete 后面一定要带 [ ] ,因为析构的是一段连续的空间,就看做是数组即可,然后再将各个迭代器置空即可;

int main()
{newVector::vector<int> v1(5,8);v1.~vector();v1.pinrt();return 0;
}

8,iterator begin()

指向第一个元素的迭代器

		iterator begin(){return _start;}

直接返回 _start 即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *v1.begin();return 0;
}

9,iterator end()

指向最后一个元素下一个元素的迭代器

		iterator end(){return _finish;}

直接返回 _finish 即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *v1.end();return 0;
}

直接随机数了;

换个思路,试一下:

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *(v1.end()-1);return 0;
}

我们找他前一个迭代器,果然是最后一个数;

10,size() const

返回有效数据个数

		size_t size() const{return _finish - _start;}

直接迭代器相减即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.size();return 0;
}

11,capacity() const

返回容量大小

		size_t capacity() const{return _endOfStorage - _start;}

直接迭代器相减即可,差值就是容量大小;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.capacity();return 0;
}

12,reserve(size_t n)

扩容

		void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t old = size();if (_start){//memcpy(tmp, _start, old * sizeof(T));for (size_t i = 0; i < old; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old;_endOfStorage = _start + n;}}

当要扩容时才用的着,先判断,然后开辟一段需要的空间,之后就是拷贝赋值了,这里我们还是没有用 memcpy 因为是浅拷贝,然后再释放原空间,再将迭代器重新赋值即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.capacity() << endl;v1.reserve(20);cout << v1.capacity() << endl;return 0;
}

一目了然;

13,resize(size_t n, const T& value = T())

更改有效数据个数,不够则填充,可以指定填充数据;

		void resize(size_t n, const T& value = T()){if (n < size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}while (_finish!=_start+n){push_back(value);}}}

当缩减数据时直接把 _finish 往前移即可,当扩容时先扩容,然后进行填充;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.size() << endl;v1.resize(10);cout << v1.size() << endl;v1.resize(15, 6);cout << v1.size() << endl;v1.pinrt();return 0;
}

可以看到,当我们不指定填充时默认填充0;

14,push_back(const T& x)

尾插

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

首先要检查是否需要扩容,然后赋值,将 _finish 往后移一位即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);v1.push_back(6);v1.push_back(7);v1.pinrt();return 0;
}

 插入十分成功;

15,pop_back()

尾删

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

像这种数组类型的,直接对其迭代器动手就行,直接 _finish 往前移一位即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);v1.pop_back();v1.pop_back();v1.pinrt();return 0;
}

删除非常顺利;

16,insert(iterator pos, const T& x)

指定位置插入

		iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);size_t old = pos - _start;if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}pos = _start + old;memcpy(pos + 1, pos, (_finish - pos) * sizeof(T));*pos = x;_finish++;return pos;}

这里会面临一个迭代器失效的问题,当扩容后,原本的 pos 还是指向旧空间就失效了,所以我们要更新 pos ;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);auto pos = find(v1.begin(), v1.end(), 1);v1.insert(pos, 9);pos= find(v1.begin(), v1.end(), 5);v1.insert(pos, 9);v1.pinrt();return 0;
}

完美插入;

17,erase(iterator pos)

擦除指定位置

		iterator erase(iterator pos){assert(pos >= 0 && pos <= _finish);memcpy(pos, pos + 1, sizeof(T)*(_finish - pos));_finish--;return pos;}

先断言判断,然后直接往前移一位覆盖即可,再更新一下 _finish ;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);auto pos = find(v1.begin(), v1.end(), 1);v1.erase(pos);pos = find(v1.begin(), v1.end(), 5);v1.erase(pos);v1.pinrt();return 0;
}

也是成功擦除了;

18,empty()

判空

		bool empty(){return _start == _finish;}

当为空时返回真,反之亦然;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);cout << v1.empty() << endl;newVector::vector<int> v2;cout << v2.empty();return 0;
}

19,operator[](size_t pos)

返回下标对应的值;

		T& operator[](size_t pos){assert(pos >= 0 && pos <= size());return _start[pos];}

直接像数组一样取值返回即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);cout << v1[0] << " " << v1[4];return 0;
}

写法也是跟数组一样简单;

20,operator= (vector<T> v)

赋值,深拷贝

		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> v){swap(v);return *this;}

直接一手交换即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);newVector::vector<int> v2 = v1;v2.pinrt();return 0;
}

 

21,总结

我们就先搞一个大概的,其中还有很多分支,比如我们写的是擦除某个数据,其实也可以擦除某个范围,这些就靠大家去摸索,查阅文档了;

vector类的实现就到这里了;

加油!

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

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

相关文章

HarmonyOS应用程序包快速修复

快速修复概述 快速修复是HarmonyOS系统提供给开发者的一种技术手段&#xff0c;支持开发者以远快于应用升级的方式对应用程序包进行缺陷修复。和全量应用升级软件版本相比&#xff0c;快速修复的主要优势在小、快和用户体验好。在较短的时间内不中断正在运行的应用的情况下&am…

【兔子王赠书第14期】《YOLO目标检测》涵盖众多目标检测框架,附赠源代码和全书彩图!

文章目录 写在前面YOLO目标检测推荐图书本书特色内容简介作者简介 推荐理由粉丝福利写在后面 写在前面 小伙伴们好久不见吖&#xff0c;本期博主给大家推荐一本关于YOLO目标检测的图书&#xff0c;该书侧重目标检测的基础知识&#xff0c;包含丰富的实践内容&#xff0c;是目标…

Qt+Opencv:人脸检测

话接上一篇&#xff0c;我们仍使用在上篇《QtOpencv&#xff1a;Qt中部署opencv》创建的Qt项目来测试opencv提供的sample。 在正式开始本篇之前&#xff0c;我们先说做一下准备工作&#xff1a; 一、opencv官方文档 学习最权威和最可靠的方式&#xff0c;就是阅读官方文档和…

QtitanRibbon 开始使用实例

新建一个界面程序&#xff1a; 修改项目里面的源码&#xff1a; 至此&#xff0c;一个简单界面就出来了&#xff0c;效果如下所示&#xff1a;

详解信道容量,信道速率,安全速率的区别

目录 一. 信道容量与信道速率 二. 小结 三. 安全速率与物理层安全 3.1 香农物理层安全模型 3.2 安全信道速率 四. 补充安全中断概率&#xff08;Secrecy Outage Probability, SOP&#xff09; 五. 补充安全分集度&#xff08;Secrecy Diversity Order, SDO&#xff09; …

数据通信网络基础的网络参考模型华为ICT网络赛道

网络参考模型 目录 网络参考模型 2.1.应用与数据 2.2.网络参考模型与标准协议 2.2.1.OSI参考模型 2.2.2.TCP/IP参考模型 2.2.3.应用层 2.2.4.传输层 2.2.5.TCP和UDP 2.2.6.网络层 2.2.7.数据链路层 2.2.8.物理层 2.3.数据通信过程 2.1.应用与数据 应用的存在&#…

2023年年度总结,一个小白的CSDN涨粉历程

前言 滚滚长江东逝水&#xff0c;一去不复返。 转眼间已到2024年节点&#xff0c;时间如滚滚长江水向东奔流不息&#xff0c;在长江消失之前&#xff0c;都不会停歇&#xff0c;也不会回头。人亦如此&#xff0c;不管是生活还是学习&#xff0c;都是不断往前走的过程&#xff…

MyBatis多表映射

1. 多表映射概念 MyBatis 思想是&#xff1a;数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式&#xff0c;可惜它们并不都是那样。 如果能有一种数据库映射模式&#xff0c;完美适配所有的应用程序查询需求&#xff0c;那就太…

【GoLang】Go语言几种标准库介绍(三)

文章目录 前言几种库debug 库 (各种调试文件格式访问及调试功能)相关的包和工具&#xff1a;示例 encoding (常见算法如 JSON、XML、Base64 等)常用的子包和其主要功能&#xff1a;示例 flag(命令行解析)关键概念&#xff1a;示例示例执行 总结专栏集锦写在最后 前言 上一篇&a…

【力扣题解】P98-验证二叉搜索树-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P98-验证二叉搜索树-Java题解&#x1f30f;题目描述&#x1f4a1;题解&#x1f30f;总…

常用环境部署(十三)——GitLab整体备份及迁移

一、GitLab备份 注意&#xff1a;由于我的GitLab是docker安装的&#xff0c;所以我的操作都是在容器内操作的&#xff0c;大家如果不是用docker安装的则直接执行命令就行。 1、Docker安装GitLab 链接&#xff1a;常用环境部署(八)——Docker安装GitLab-CSDN博客 2、GitLab备…

2024年腾讯云优惠券领取及使用指南

​ 腾讯云&#xff08;Tencent Cloud&#xff09;作为中国领先的云计算服务商&#xff0c;已为全球数百万客户提供云服务器、云数据库、云存储、CDN、人工智能等云计算服务。本文将介绍如何领取腾讯云优惠券&#xff0c;以及在使用过程中的注意事项。 一、腾讯云优惠券介绍 腾…