【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.rhkb.cn/news/228365.html

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

相关文章

读书笔记1-C++ Primer Plus

C是在C语言基础上开发的一种集面向对象编程&#xff08;OOP&#xff09;、通用编程和传统的过程化编程于一体的编程语言。本书是根据2003年的ISO/ANSI C标准编写的&#xff0c;通过大量短小精悍的程序详细而全面地阐述了C的基本概念和技术。 全书分17章和10个附录&#xff0c;分…

深度学习——PIL和OpenCV

PIL 官方文档 格式互转 opencv cv2.imread() 参数&#xff1a; filepath&#xff1a;读入imge的完整路径 flags&#xff1a;标志位&#xff0c;{cv2.IMREAD_COLOR&#xff0c;cv2.IMREAD_GRAYSCALE&#xff0c;cv2.IMREAD_UNCHANGED} cv2.IMREAD_COLOR&#xff1a;默认参数&…

Leetcode每日一题:1599.经营摩天轮的最大利润

前言&#xff1a;本题是一道逻辑细节题&#xff0c;考察阅读理解并转化为代码的能力&#xff0c;很多细节 题目描述&#xff1a; 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0c;每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱&#xff0c;但…

Jetpack Compose中使用Android View

使用AndroidView创建日历 Composable fun AndroidViewPage() {AndroidView(factory {CalendarView(it)},modifier Modifier.fillMaxWidth(),update {it.setOnDateChangeListener { view, year, month, day ->Toast.makeText(view.context, "${year}年${month 1}月$…

JSON 详解

文章目录 JSON 的由来JSON 的基本语法JSON 的序列化简单使用stringify 方法之 replacerstringify 方法之 replacer 参数传入回调函数stringify 方法之 spacestringify 方法之 toJSONparse 方法之 reviver 利用 stringify 和 parse 实现深拷贝 json 相信大家一定耳熟能详&#x…

Apache Doris (五十五): Doris Join类型 - Colocation Join

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Colocation Join原理

Hive讲课笔记:内部表与外部表

文章目录 零、学习目标一、导言二、内部表1.1 什么是内部表1.1.1 内部表的定义1.1.2 内部表的关键特性 1.2 创建与操作内部表1.2.1 创建并查看数据库1.2.2 创建数据表1.2.3 插入表记录1.2.4 通过HDFS WebUI查看数据库与表 三、外部表2.1 什么是外部表2.2 创建与操作外部表2.2.1…

Idea如何从磁盘中应用 下载好的插件流程,安装zip压缩包。

1、将下载的插件文件&#xff08;通常是一个ZIP文件&#xff09;复制到IntelliJ IDEA的“plugins”文件夹中。 IDEA版本 2、重启IntelliJ IDEA。 3、在设置窗口中&#xff0c;选择左侧的“Plugins”。 4、选择之前复制到“plugins”文件夹中的插件文件&#xff0c;点击“OK”按…

【新手向】VulnHub靶场MONEYBOX:1 | 详细解析

MONEYBOX:1 安装靶机 作为一名新手&#xff0c;首先要配置好环境&#xff0c;才能进行下一步的操作。 将下载的ova文件导入VirtualBox。 VirtualBox下载地址&#xff1a;https://www.oracle.com/cn/virtualization/technologies/vm/downloads/virtualbox-downloads.html 选择…

JMeter(十六)-JMeter断言

十六、JMeter断言 1.简介 断言组件用来对服务器的响应数据做验证&#xff0c;常用的断言是响应断言&#xff0c;其支持正则表达式。虽然我们的通过响应断言能够完成绝大多数的结果验证工作&#xff0c;但是JMeter还是为我们提供了适合多个场景的断言元件&#xff0c;辅助我们来…

【数据结构】栈和队列(队列的基本操作和基础知识)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 ​ 目录 前言 队列 队列的概念和结构 队列的…

【操作系统】虚拟存储器

5.1 虚拟存储器概述 之前介绍的各种存储器管理方式有一个共同的特点&#xff0c;即它们都要求将一个作业全部装入内存后方能运行。于是&#xff0c;出现了下面这样两种情况&#xff1a; (1) 有的作业很大&#xff0c;其所要求的内存空间超过了内存总容量&#xff0c;作业不能全…

【PTA-C语言】实验七-函数与指针I

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 目录——实验七-函数与指针I 6-1 弹球距离&#xff08;分数 10&#xff09;6-2 使用函数输出一个整数的逆序数&#xff08;分数 10&#xff09;6-3 使用函数求最大公约数&#xff08;分数 10&#xff09;6-4…

用CSDN训练的InsCode AI创作博文:数据治理体系建设

想不想用AI帮我们写方案&#xff1f; 想尝试用CSDN提供的InsCode AI创作助手协助我们进行技术方案的创作&#xff0c;看看效果如何&#xff0c;能不能辅助我们日常的方案编写与创作&#xff1f;以前用ChatGPT也尝试过&#xff0c;但对于专业性更强的内容&#xff0c;还有表现的…

BIOS:计算机中的特洛伊木马

内容概述&#xff1a; 由于主板制造商在计算机启动时用来显示品牌徽标的图像分析组件相关的问题&#xff0c;多个安全漏洞&#xff08;统称为 LogoFAIL&#xff09;允许攻击者干扰计算机设备的启动过程并安装 bootkit。x86 和 ARM 设备都面临风险。主板固件供应链安全公司 Bin…

软件工程总复习笔记

软件工程课程复习提纲 文章目录 软件工程课程复习提纲一、基本知识点1. 软件工程的概念及目标2. 软件危机的概念及典型表现3. 瀑布模型的概念及特点4. 快速原型模型的特点5. 螺旋模型的基本思想6. 软件生命周期的概念及划分为哪几个阶段7. 软件需求的定义8. 常见的软件需求获取…

html-css-js移动端导航栏底部固定+i18n国际化全局

需求&#xff1a;要做一个移动端的仿照小程序的导航栏页面操作&#xff0c;但是这边加上了i18n国家化&#xff0c;由于页面切换的时候会导致国际化失效&#xff0c;所以写了这篇文章 1.效果 切换页面的时候中英文也会跟着改变&#xff0c;不会导致切换后回到默认的语言 2.实现…

R_handbook_作图专题

ggplot基本作图 1 条形图 library(ggplot2) ggplot(biopics) geom_histogram(aes(x year_release),binwidth1,fill"gray") 2 堆砌柱状图 ggplot(biopics, aes(xyear_release)) geom_bar(aes(fillsubject_sex)) 3 堆砌比例柱状图 ggplot(biopics, aes(xyear_rele…

DSL查询语法和RestClient查询文档

目录 DSL查询语法 DLS Query的分类 DSL Query基本语法 全文检索查询 精准查询 地理查询 复合查询 Function Score Query 复合查询 Boolean Query 搜索结果处理 排序 分页 分页 深度分页问题 深度分也解决方案 高亮 RestClient查询文档 快速入门 全文检索查…

sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set问题解决方案

sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set问题解决方案 当我们使用sudo su切换权限时提示错误&#xff1a; sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set该错误出现原因&#xff1a;是因为/usr/bin/sudo的权限被…