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://xiahunao.cn/news/2906597.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式3-29

今日作业&#xff1a;用fwrite 和 fseek功能&#xff0c;将一张bmp格式的图片更改成 德国国旗#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> typedef unsigned char bgr[3]; int main(int argc, const char *argv[])…

基于JSPM的美食推荐管理系统

背景 互联网的迅猛扩张彻底转变了全球各类组织的运营模式。自20世纪90年代起&#xff0c;中国各级政府和企事业单位便开始探索运用网络系统来处理管理事务。然而&#xff0c;早期的网络覆盖不广、用户接受度不高、相关网络法规不健全以及技术发展不成熟等因素&#xff0c;都曾…

Vue-vue3

一、Vue3简介二、Vue3有那些优化性能的提升源码升级拥抱TypeScript新的特性 三、创建Vue3.0工程四、Vue3工程结构&#xff08;使用cli创建的vue3&#xff09;五、常用的Composition API&#xff08;组合式API&#xff09;setupsetup的两个注意点 ref函数reactive函数Vue3.0中的…

鸿蒙原生应用开发-网络管理HTTP数据请求

一、场景介绍 应用通过HTTP发起一个数据请求&#xff0c;支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。 二、接口说明 HTTP数据请求功能主要由http模块提供。 使用该功能需要申请ohos.permission.INTERNET权限。 涉及的接口如下表&#xff0c;具体的…

C/C++中重载函数取地址的方法

目录 1.现象 2.指定参数取函数地址 3.利用Qt的类QOverload 1.现象 函数重载在C/C编码中是非常常见的&#xff0c;但是我们在std::bind或std::function绑定函数地址的时候&#xff0c;直接取地址&#xff0c;程序编译就会报错&#xff0c;示例如下&#xff1a; class CFunc1…

2024年妈妈杯数学建模思路A题B题C题D题思路分享

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

鸿蒙迎来大爆发,有必要转行鸿蒙开发吗?

鸿蒙系统&#xff0c;作为华为自主研发的操作系统&#xff0c;正在迎来前所未有的大爆发&#xff0c;以强大的分布式技术和创新能力&#xff0c;不仅支持多种终端设备&#xff0c;更在构建一个日益完善的生态系统&#xff0c;在当前的科技浪潮中。面对这样的发展趋势&#xff0…

Git版本管理使用手册 - 8 - 合并分支、解决冲突

合并整个开发分支 切换到本地test分支&#xff0c;选择右下角远程开发分支&#xff0c;选择Merge into Current。然后提交到远程test仓库。 合并某次提交的代码 当前工作区切换成test分支&#xff0c;选择远程仓库中的dev开发分支&#xff0c;选择需要合并的提交版本右击&a…

Python基础:标准库 -- pprint (数据美化输出)

1. pprint 库 官方文档 pprint --- 数据美化输出 — Python 3.12.2 文档 pprint — Data pretty printer — Python 3.12.2 documentation 2. 背景 处理JSON文件或复杂的嵌套数据时&#xff0c;使用普通的 print() 函数可能不足以有效地探索数据或调试应用程序。下面通过一…

linux提权笔记

1 linux提权简介 Linux提权&#xff0c;简单来说&#xff0c;就是用户尝试获取高于其当前权限级别的系统访问权限的过程。在Linux系统中&#xff0c;root用户拥有最高的权限&#xff0c;能够执行任何操作&#xff0c;包括修改系统文件、安装软件、管理用户账户等。而普通用户通…

Java基础语法(五)| 方法

1. 方法概述 1.1 什么是方法 方法&#xff08;method&#xff09;完成某一个特定功能的代码块。 1.2 方法基本使用 将资料中给大家提供的打怪物发射炮弹重复的代码&#xff0c;把一些重复代码进行抽取&#xff08;封闭&#xff09;思想. 调用格式 方法名(); 注意&#xff1a; …

redis集群配置(精华版):哨兵模式

哨兵模式 概念单机单个哨兵多哨兵模式 动手实操1、环境准备2、配置sentinel.conf配置文件3、启动哨兵&测试4、SpringBoot测试哨兵模式故障转移功能 概念 主从切换技术的方法是&#xff1a;当主服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;这…

无人驾驶矿卡整体解决方案(5g物联网通信方案)

​无人驾驶矿卡是智能矿山的重要组成部分,通过远程操控替代人工驾驶,可以显著提高采矿效率和作业安全性。但要实现无人驾驶矿卡,需要依赖于可靠高效的通信网络,来传输现场视频、控制指令和运行数据。以下是某大型煤矿在部署无人驾驶矿卡时,所采用的星创易联物联网整体解决方案。…

【C++的奇迹之旅】C++关键字命名空间使用的三种方式C++输入输出命名空间std的使用惯例

文章目录 &#x1f4dd;前言&#x1f320; C关键字(C98)&#x1f309; 命名空间&#x1f320;命名空间定义&#x1f309;命名空间使用 &#x1f320;命名空间的使用有三种方式&#xff1a;&#x1f309;加命名空间名称及作用域限定符&#x1f320;使用using将命名空间中某个成员…

第十四届蓝桥杯JavaA组省赛真题 - 特殊日期

解题思路&#xff1a; 暴力秒了 public class Main {public static void main(String[] args) {int cnt 0;for (int i 1900; i < 9999; i) {for (int j 1; j < 12; j) {for (int k 1; k < days(i, j); k) {if (sum(i) sum(j) sum(k)) cnt;}}}System.out.print…

Nginx漏洞之未授权访问和源码泄漏漏洞处理

一、漏洞描述 某次安全扫描&#xff0c;发现某平台存在资源&#xff1a;未授权访问和源码泄漏&#xff1b;攻击者可能获取到网站的配置文件、敏感数据存储位置和访问凭证等信息。这意味着攻击者可以获得对网站的完全或部分控制权&#xff0c;进而进行恶意篡改、删除或添加恶意…

Linux-进程控制

&#x1f30e;进程控制【上】 文章目录&#xff1a; 进程控制 为什么要有地址空间和页表 程序的内存       程序申请内存使用问题 写时拷贝与缺页中断 父子进程代码共享       为什么需要写时拷贝       页表的权限位       缺页中断 退出码和错误码…

SpringBoot 登录认证(二)

SpringBoot 登录认证&#xff08;一&#xff09;-CSDN博客 SpringBoot 登录认证&#xff08;二&#xff09;-CSDN博客 SpringBoot登录校验&#xff08;三&#xff09;-CSDN博客 HTTP是无状态协议 HTTP协议是无状态协议。什么又是无状态的协议&#xff1f; 所谓无状态&…

毅速丨一体化压铸浪潮涌动 3D打印压铸模具将迎来广泛应用

近年来&#xff0c;特斯拉等车企的革新步伐引领着汽车制造业的变革&#xff0c;车身一体化压铸技术日益凸显其地位。这一技术不仅简化了车身制造的繁琐流程&#xff0c;更在供应链整合上实现了革命性的突破&#xff0c;为汽车制程带来了前所未有的经济性提升。 压铸技术&#x…

【面试那些事儿】浏览器键入网址到网页显示,期间发生了什么

当用户在浏览器中键入网址并按下回车键后&#xff0c;直到最终显示出完整的网页内容&#xff0c;这一过程中经历了哪些步骤呢&#xff1f; 这是一道经典的面试题&#xff0c;我们来看看具体有哪些阶段。 1. 解析URL 浏览器首先解析用户输入的URL&#xff0c;判断它的语法和协…