当前位置: 首页 > 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…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...