C++——vector类及其模拟实现

前言:前边我们进行的string类的方法及其模拟实现的讲解。这篇文章将继续进行C++的另一个常用类——vector。


一.什么是vector

vector和string一样,隶属于C++中STL标准模板库中的一个自定义数据类型,实际上就是线性表两者之间有着很多相似,甚至相同的方法

但是它们也有着很大的不同:因为vector是线性表,所以他可以存储任意类型的数据,甚至是自定义类型的数据,包括vector本身


二.vector基本框架

#include<assert.h>namespace Myvector
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//构造函数voctor(){}//析构函数~voctor(){delete[] _start;_strat = _finish = _endofstorage = nullptr;}//迭代器iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}//数据个数size_t size() const{return _finish - _start;}//空间大小size_t capacity() const{return _endofstorage - _start;}//[]运算符重载T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

因为vector允许定义不同类型的数据,所以我们要使用模版来定义类

值得注意的是,在voctor中我们不用传统的size和capacity来定义数据个数和总空间大小,而是都将它们定义为指针类型,通过指针间的相减来得到size和capacity。初始情况下三者均为空指针。

同时我们将T*指针类型重定义为iterator,这是因为vector的本质就是数组,其迭代器就是指针


三.vector常用操作

1.扩容

扩容分为两种方式,只扩容不初始化的reserve,以及扩容并且初始化的resize调整

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

扩容较为简单,值得注意的是这里不在使用strcpy,而是通过for循环的方式进行赋值。

		//调整void resize(size_t n,const T& val = T()){if (n > size()){reserve(n);while (_finish < _start + n){*_finish = val;_finish++;}}else{_finish = _start + n;}}

对于resize,因为我们需要使用缺省参数,但是由于vector的对象类型不定,甚至是自定义类型,所以我们不能使用0作为缺省参数而应使用匿名对象作为参数,让匿名对象去自动识别类型并调用自己的构造函数形成初始值


2.插入

先来看尾插,与string类似,需要考虑扩容:

		//尾插void push_back(const T& val){if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;_finish++;}

测试如下:

 在来看指定位置的插入先判断pos是否合法,同样需要判断扩容

		//指定位置插入void insert(iterator pos, const T& val){assert(pos >= _start);assert(pos < _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//扩容要更新pospos = _start + len;}iterator it = _finish - 1;while (it >= pos){*(it + 1) = *it;--it;}*pos = val;_finish++;}

这个指定的位置pos用迭代器比用数字更加方便进行比较,所以建议直接使用迭代器进行插入

有一点值得注意的是,传入的pos是指向原数组的如果进行了扩容,那么原数组被销毁,所有的指针都指向新的数组,所以也需要对pos指针进行更新,否则pos就会成为野指针。 

 测试如下:


3.遍历

除了上边的for循环遍历外,我们还可以通过迭代器和范围for进行遍历

		vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << ' ';++it;}cout << endl;for (auto e : v){cout << e << ' ';}cout << endl;

测试如下:


4.删除

首先来看尾删,和顺序表一样,尾删就直接让_finish减1即可,还要注意判断是否为空

		//判空bool empty(){return _start == _finish;}//尾删void pop_back(){assert(!empty());--_finish;}

测试如下:

 再来看指定位置的删除:

		//指定位置删除void erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}_finish--;}

测试如下:


5.交换 

想要实现两个对象之间的交换,其底层实现还是要借助std库中的swap函数

		//交换void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}

测试如下: 

 


6.赋值

使用容器的一种必不可少的方法就是相同类型的对象之间的赋值,这就关系到类中的拷贝构造函数了。

我们知道拷贝构造分为浅拷贝和深拷贝后者要关系到指针,显然,voctor类型对象的成员变量都是指针,所以我们必须写出深拷贝构造函数

		//拷贝构造函数vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}

这里我们采用了先开空间,在依次尾插的方式来实现拷贝构造函数,测试如下:


还可以使用“=”运算符重载的方式进行赋值:

		//=运算符重载vector<T>& operator=(vector<T> v){swap(v);return *this;}

这个写法非常的高明,假设赋值为:v1 = v2,那么v将作为v2的拷贝与v1进行交换,然后返回v1,而v因为只是一个拷贝,出函数后就会被销毁,所以也不会影响到v2的值,测试如下:

 


 此外,还有一种方式,他可以选择某个对象的部分区间赋值给新的对象,叫做迭代器区间构造

		//迭代器区间构造template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}

 这里要注意,该函数我们把它写成模版函数

类模板的成员函数可以是函数模版,那么写成这样的好处是什么呢?

能够看出,我们不仅能截取同为vector类型的对象,还能够截取不同的string类型的对象。 


总结

关于vector的分享就到这里啦。

喜欢本篇文章的小伙伴记得一键三连,我们下期再见!

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

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

相关文章

C++初阶:模拟实现vector类

模拟实现vector类 实现代码:vector.h #pragma once #include<assert.h> #include<iostream> using namespace std;namespace bit {template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_ite…

【多线程系列】你先说说synchronized的实现原理

面试官&#xff1a;听说你精通多线程&#xff0c;那我就考考你吧 面试官&#xff1a;不用慌尽管说&#xff0c;错了也没关系&#x1f60a;。。。 以贴近现实的【面试官面试】形式来分享技术&#xff0c;本期是《多线程系列》&#xff0c;感兴趣就关注我吧❤️ 面试官&#xff1…

FME学习之旅---day16

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 【FME-HOW-TO系列】24 拓扑相交 本章还是学习SpatialFilter转换器得用法&#xff0c;主要用到的空间关系是相交&#xff0c;即Filter Intersect Candidate。 首先&#xff0c;添加读模块的相…

王道C语言督学营OJ课后习题(课时14)

#include <stdio.h> #include <stdlib.h>typedef char BiElemType; typedef struct BiTNode{BiElemType c;//c 就是书籍上的 datastruct BiTNode *lchild;struct BiTNode *rchild; }BiTNode,*BiTree;//tag 结构体是辅助队列使用的 typedef struct tag{BiTree p;//树…

Redis 主从复制原理,设计的真巧妙!

前言 今天继续来看看有关 Redis 的一个问题&#xff0c;主从复制。通常&#xff0c;对于大多数的场景来说&#xff0c;读比写更多&#xff0c;于是对于缓存的水平扩展&#xff0c;其中的一个方式 “主从复制” 就是一个常见的思路。有了主从复制&#xff0c;那么可以扩展出很多…

android studio忽略文件

右键文件&#xff0c;然后忽略&#xff0c;就不会出现在commit里面了 然后提交忽略文件即可

基于Python微博舆情数据爬虫可视化分析系统(NLP情感分析+爬虫+机器学习)

这里写目录标题 基于Python微博舆情数据爬虫可视化分析系统(NLP情感分析爬虫机器学习)一、项目概述二、微博热词统计析三、微博文章分析四、微博评论分析五、微博舆情分析六、项目展示七、结语 基于Python微博舆情数据爬虫可视化分析系统(NLP情感分析爬虫机器学习) 一、项目概…

【Docker】Docker安全与最佳实践:保护你的容器化应用程序

欢迎来到英杰社区&#xff1a; https://bbs.csdn.net/topics/617804998 欢迎来到阿Q社区&#xff1a; https://bbs.csdn.net/topics/617897397 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff…

【目标跟踪】红绿灯跟踪

文章目录 一、前言二、结果三、跟踪3.1、检测输入3.2、预测与运动补偿3.3、第一次匹配3.4、第二次匹配3.5、第三次匹配3.6、航迹的起始与信息的发布 四、后记 一、前言 红绿灯场景对当前无人驾驶来说是个灾难性的挑战。暂且不说复杂的十字路口&#xff0c;譬如简单的人行道红绿…

括号生成(回溯+剪枝)

22. 括号生成 - 力扣&#xff08;LeetCode&#xff09; 题目描述 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 样例输入 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))&q…

Java毕业设计 基于SSM新闻管理系统

Java毕业设计 基于SSM新闻管理系统 SSM jsp 新闻管理系统 功能介绍 用户&#xff1a;首页 图片轮播 查询 登录 注册 新闻正文 评论 广告 社会新闻 天下新闻 娱乐新闻 个人中心 个人收藏 管理员&#xff1a;登录 用户管理 新闻管理 新闻类型管理 角色&#xff1a;用户 管理员…

鸿蒙OS开发实例:【瀑布流式图片浏览】

介绍 瀑布流式展示图片文字&#xff0c;在当前产品设计中已非常常见&#xff0c;本篇将介绍关于WaterFlow的图片浏览场景&#xff0c;顺便集成Video控件&#xff0c;以提高实践的趣味性 准备 请参照[官方指导]&#xff0c;创建一个Demo工程&#xff0c;选择Stage模型熟读Har…