当前位置: 首页 > news >正文

C++_vector类

欢迎来到本期节目- - -

vector类

本期直接先上代码,然后以代码为例介绍需要注意的问题.


模拟实现:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace my_room
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin()const{return _start;}const_iterator cend() const{return _finish;}
//---------------------------------------------------------------------------------------------//构造和析构//无参默认构造vector()	{//由于给了缺省值,所以可以给空}//带参构造vector(size_t n, const T& value = T())	{reserve(n);while (n--){push_back(value);}}//迭代器区间构造template<class InputIterator>vector(InputIterator first, InputIterator last)		{size_t size = last - first;reserve(size);		//提前开好空间,减少扩容次数while (first != last){push_back(*first);first++;}}//拷贝构造vector(const vector<T>& v)			{vector<T> tmp(v.cbegin(), v.cend());//直接复用swap(tmp);}//赋值重载拷贝vector<T>& operator=(vector<T> v)	{swap(v);return *this;}~vector(){delete[] _start;	//为空也不影响_start = _finish = _endOfStorage = nullptr;}//----------------------------------------------------------------------------------------------//核心函数void reserve(size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + old_size;_endOfStorage = _start + n;}}void resize(size_t n, const T& value = T()){reserve(n);if (n <= size()){_finish = _start + n;}else{while (_finish != _start + n){*(_finish++) = value;}}}iterator insert(iterator pos, const T& x){assert(pos <= end());size_t old_posi = pos - begin();if (_finish == _endOfStorage){size_t n = capacity() == 0 ? 4 : capacity() * 2;reserve(n);}iterator tail = end();pos = begin() + old_posi;while (tail > pos){*tail = *(tail - 1);tail--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos < end());iterator begin = pos+1;while (begin != _finish){*(begin-1) = *begin;begin++;}_finish--;return pos;}//----------------------------------------------------------------------------------------------//简单函数size_t size() const{return _finish - _start;}size_t capacity() const{return _endOfStorage - _start;}bool empty()const{return _start == _finish;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}void push_back(const T& x){insert(end(), x);}void pop_back(){erase(end() - 1);}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}private:iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};
}

核心函数

reserve

首先我们看看reserve函数
当我们在实现vector时,我们都知道,实现异地扩容都要经历这个流程:

异地扩容
申请新空间
拷贝数据
释放旧空间
指向新空间
更新属性

注意

在这其中,拷贝数据就需要注意,在以往的顺序表中,我们可能会用memcpy来拷贝数据,当然在内置类型的情况下,memcpy不仅不会出错,而且比赋值拷贝高效;
但是这里是类模板,数据类型T可以是自定义类型,就有可能需要深拷贝,而memcpy本质上是浅拷贝,不满足需求,所以使用赋值重载拷贝完成自定义类型的拷贝.

除此之外,还需要注意的是更新属性问题,

_finish = _start+size();	//size()返回的是 (_finish - _start)的值
此时_finish指向旧空间,_start指向新空间,返回的值根本不是有效数据的个数;
所以需要在扩容之前记录一个old_size记录数据个数,使_finish指向有效数据的末尾.
 void reserve(size_t n){if (n > capacity()){size_t old_size = size();			//记录old_size是为了不影响有效数据末尾的正确计算T* tmp = new T[n];for (size_t i = 0; i < old_size; i++)		{tmp[i] = _start[i];				//使用赋值拷贝是为了满足有深拷贝的自定义类型对象}delete[] _start;_start = tmp;_finish = _start + old_size;_endOfStorage = _start + n;}}

resize

接下来瞧瞧resize
resize只需要注意,该函数会改变有效数据的个数以及有可能会扩容.

void resize(size_t n, const T& value = T())
{reserve(n);		//管他容量够不够,直接让reserve去判断.if (n <= size()){_finish = _start + n;	//直接删除数据}else{while (_finish != _start + n)	{*(_finish++) = value;		//添加数据}}
}

insert

接下来瞅瞅insert
在实现insert函数时,核心逻辑就是移动数据.

iterator insert(iterator pos, const T& x)
{assert(pos <= end());		//注意可以尾插,所以取等size_t old_posi = pos - begin();			//由于可能需要扩容,所以会导致迭代器指向旧空间,提前记录相对位置if (_finish == _endOfStorage){size_t n = capacity() == 0 ? 4 : capacity() * 2;reserve(n);}iterator tail = end();pos = begin() + old_posi;		//更新迭代器while (tail > pos){*tail = *(tail - 1);		//从后往前挨个 向后移动tail--;}*pos = x;		//插入数据_finish++;return pos;
}

erase

最后瞄一下erase

iterator erase(iterator pos)
{assert(pos < end());		//_finish没有数据,不能取等iterator begin = pos+1;while (begin != _finish){*(begin-1) = *begin;			//从前往后挨个  向前移动begin++;}_finish--;return pos;
}

迭代器

迭代器:

迭代器屏蔽了底层的实现细节,使得设计人员无需关心容器对象的内存分配就可以直接使用相应接口,达到类似指针的效果.

也就是说迭代器可能是指针,也可能是指针的封装.

迭代器类型
输入输出迭代器
随机迭代器
Random-access Iterator
双向迭代器
Bidirectional Iterator
前向迭代器
Forward Iterator
Input Iterator
Output Iterator

上图中迭代器支持的行为是向下兼容的,
假设p1,p2是迭代器,i是常量

Iterator描述行为
随机迭代器支持随机访问的迭代器p1++,p1- -,p1+i,p1-i,p1==p2,p1>p2,p1<p2
双向迭代器可以向前或者向后移动的迭代器p1++,p1- -,p1==p2
前向迭代器只能向前移动的迭代器p1++,p1==p2
输入迭代器只能向前移动并读取的迭代器p1++,p1==p2
输出迭代器只能向前移动并写入的迭代器p1++ ,p1==p2

很明显咱们的vector的迭代器是随机迭代器.

迭代器失效

什么是迭代器失效?
迭代器所指向的节点无效了,既该节点被删除了.

vector迭代器失效场景
  • 场景1:扩容
void test()
{my_room::vector<int> arr;for(int i = 0; i < 10; i++)	//存入1到10{arr.push_back(i+1);}my_room::vector<int>::iterator old_back = arr.end()-1;	//指向最后一个元素10arr.reserve(1000);cout<< *old_back <<endl;	//程序崩溃----error代码,请勿模仿
}
扩容之后,old_back指向的空间已经被释放了,此时相当于使用野指针.
事实上,不一定只能通过直接显示调用reserve导致迭代器失效,也可以间接调用reserve发生扩容行为导致迭代器失效;
例如: 使用resize函数可能会扩容,使用insert,push_back函数也可能扩容
总的来说,就是底层涉及扩容的行为,就需要注意迭代器失效的问题.
  • 场景2:删除
void test()
{my_room::vector<int> arr;for(int i = 0; i < 10; i++)	//存入1到10{arr.push_back(i+1);}my_room::vector<int>::iterator  start = arr.begin();	//指向第一个元素1while(start != arr.end())	//预期删除所有元素{arr.erase(start);	//------error代码,请勿模仿}cout << arr.size();	//看看有没有删完,但实际上在vs中直接报错.
}
本来预期的是start一开始指向第一个元素,删除之后,剩下的元素往前移,然后继续删,直到删完.
但是实际上删除行为同样也有迭代器失效的风险.

在这里插入图片描述

迭代器失效解决方案

我们知道在使用vector时,当接口涉及扩容问题或者删除问题时,会有迭代器失效的问题.
解决办法就是使用之前对迭代器重新赋值.

void test()
{my_room::vector<int> arr;for(int i = 0; i < 10; i++){arr.push_back(i+1);}my_room::vector<int>::iterator old_back = arr.end()-1;	arr.reserve(1000);old_back = arr.end()-1;cout<< *old_back <<endl;	//程序正常运行
}
void test()
{my_room::vector<int> arr;for(int i = 0; i < 10; i++)	{arr.push_back(i+1);}my_room::vector<int>::iterator  start = arr.begin();while(start != arr.end()){start = arr.erase(start);	}cout << arr.size();	//程序正常运行
}

希望本片文章对您有帮助,请点赞支持一下吧😘💕

相关文章:

C++_vector类

欢迎来到本期节目- - - vector类 本期直接先上代码&#xff0c;然后以代码为例介绍需要注意的问题. 模拟实现&#xff1a; #pragma once #include<iostream> #include<assert.h> using namespace std;namespace my_room {template<class T>class vector{p…...

Spring Boot入门到精通:网上购物商城系统

第3章 系统分析 3.1 可行性分析 在系统开发之初要进行系统可行分析&#xff0c;这样做的目的就是使用最小成本解决最大问题&#xff0c;一旦程序开发满足用户需要&#xff0c;带来的好处也是很多的。下面我们将从技术上、操作上、经济上等方面来考虑这个系统到底值不值得开发。…...

在Vue.js中,你可以使用Element UI的el-input组件结合计算属性来实现模糊查询

<template><div><el-input v-model"searchQuery" placeholder"请输入查询内容"></el-input><div v-for"item in filteredList" :key"item">{{ item }}</div></div> </template><s…...

delphi制作漂亮的农历窗体(IntraWeb+Layui的完美结合)

delphi制作漂亮的农历窗体&#xff08;IntraWebLayui的完美结合&#xff09; 不需要安装服务器&#xff0c;Apache和IIS都不需要&#xff0c;自带企业级服务器。 运行exe服务器就架好了&#xff0c;直接打开手机浏览器或者电脑浏览器&#xff0c;网页就出来了&#xff0c;如果…...

发票OFD格式转换成PDF

引入依赖&#xff0c;低版本的报错&#xff0c;2.0.2能够实现转换 <dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-converter</artifactId><version>2.0.2</version><exclusions><exclusion><groupId&g…...

高通AI应用程序开发3:网络模型(一)

1. 支持的网络模型 Qualcomm神经处理SDK支持下表所列的网络模型。 有关支持的运行时和单个图层类型的限制和约束的详细信息&#xff0c;请参阅 限制 。 GPU运行时中支持的所有层对两种GPU模式都有效&#xff1a;GPU_FLOAT32_16_HYBRID和GPU_FLAAT16。GPU_FLOAT32_16_HYBRID-…...

03. 前端面试题之ts : typescript 的数据类型有哪些?

文章目录 一、typescript是什么二、typescript有哪些数据类型booleannumberstringarraytupleenumanynull 和 和 undefinedvoidneverobject 三、总结 一、typescript是什么 typescript 和 javascript几乎一样&#xff0c;拥有相同的数据类型&#xff0c;另外在javascript基础上…...

PyCharm和VS Code 安装通义灵码,可本地安装包安装,解决插件安装不上问题

PyCharm和VS Code 安装通义灵码&#xff0c;可本地安装包安装&#xff0c;解决插件安装不上问题 PyCharm、VS Code 安装通义灵码介绍主要应用场景支持编程语言安装指南JetBrains IDEs 中安装指南步骤 1&#xff1a;准备工作步骤 2&#xff1a;在 JetBrains IDEs 中安装通义灵码…...

机器人速度雅可比矩阵求解(2自由度平面关节机器人)

关节速度和末端速度空间的映射需要计算雅可比矩阵的逆矩阵,在博途PLC里如何计算一个方阵的逆矩阵,大家可以参考下面这篇文章: 博途PLC矩阵求逆 矩阵求逆 博图SCL_博图矩阵运算-CSDN博客文章浏览阅读839次。本文介绍如何用C语言实现矩阵求逆的过程,详细解析了相关代码,适…...

【AI大模型-文心-思维树解读-开篇】

提问&#xff1a;什么是“”“思维树”“”模型框架 回答&#xff1a;如下 版本&#xff1a;文心大模型3.5 “思维树”&#xff08;Tree of Thoughts, ToT&#xff09;模型框架是一个利用大型语言模型进行问题解决的框架。它借鉴了人类认知研究的成果&#xff0c;特别是关于人…...

2、electron vue3 怎么创建子窗口,并给子窗口路由传参

接上回初始化vue3 electron项目&#xff0c;创建完vue3 electron项目后&#xff0c;现在要实现在渲染进程中点击按钮创建一个新的子窗口 开始 子窗口创建操作只能在主线程内完成&#xff0c;而创建操作是在渲染线程触发&#xff0c;因此就需要进行两者间的通讯。 1、创建子窗…...

8.pod数据持久化

&#x1f482; 个人主页: Java程序鱼 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏(一键三连)和订阅专栏 &#x1f464; 微信号&#xff1a;hzy1014211086&#xff0c;想加入技术交流群的小伙伴可以加我好友&#xff0c;群里会分享学习资料、学习方法…...

C语言 | Leetcode C语言题解之第436题寻找右区间

题目&#xff1a; 题解&#xff1a; typedef struct {int start;int index; } Node;int cmp(const void *pa, const void *pb) {return ((Node *)pa)->start - ((Node *)pb)->start; }int* findRightInterval(int** intervals, int intervalsSize, int* intervalsColSiz…...

SpringBoot3中ymal配置文件(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;JavaWeb关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 在SpringBoot项目中,使用application.properties进行配置管理时&#xff0c;…...

Linux 基础IO 2

读取与写入 read与fread 在基础IO 1中我们学会了open和fopen的函数这两个函数是用于为进程打开文件也可以理解为为进程和文件建立了一个链接使其可以交互。那我们建立号链接之后肯定还是需要对文件进行操作&#xff0c;现在我们先来了解读取操作。 read&#xff1a; 这是一…...

图像预处理 图像去噪之常见的去噪方法

图像去噪是图像预处理中的一项关键技术&#xff0c;其目的是从含有噪声的图像中恢复出无噪声的图像&#xff0c;以提高图像质量和后续图像分析的准确性。图像去噪方法众多&#xff0c;本文将介绍几种常见的去噪方法&#xff0c;并提供相应的代码示例。 1. 均值滤波&#xff08…...

代码随想录Day53|102.沉没孤岛 、103.水流问题 、104.建造最大岛屿

102.沉没孤岛 import java.util.*;class Main{public static int[][] dir {{0,1},{1,0},{0,-1},{-1,0}};public static void main (String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int m sc.nextInt();int[][] grid new int[n][m];for(int i 0…...

19c-pfile

经常需要rman恢复测试&#xff0c;创建一个单机pfile&#xff0c;需要时手动修改使用&#xff0c;以20g内存为例 orcl.__data_transfer_cache_size0 orcl.__db_cache_size13824425984 orcl.__inmemory_ext_roarea0 orcl.__inmemory_ext_rwarea0 orcl.__java_pool_size0 orcl._…...

智能软件开启精准品牌控价

在当今竞争激烈的商业世界中&#xff0c;品牌的价值如同璀璨的明珠&#xff0c;需要精心呵护。而价格管控&#xff0c;则是守护这颗明珠的关键防线。 当面对众多的产品和 SKU 时&#xff0c;传统的人力监测已显得力不从心。此时&#xff0c;力维网络自主开发的数据监测系统如同…...

OpenCV特征检测(8)检测图像中圆形的函数HoughCircles()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在灰度图像中使用霍夫变换查找圆形。 该函数使用霍夫变换的一种修改版本在灰度图像中查找圆形。 例子&#xff1a; #include <opencv2/imgp…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...