深度学习 vector 之模拟实现 vector (C++)
1. 基础框架
这里我们有三个私有变量,使用 _finish - _start 代表 _size,_end_of_storage - _start 代表 _capacity,并且使用到了模版,可以灵活定义存储不同类型的 vector,这里将代码量较小的函数直接定义在类的内部使其成为内联函数
namespace bit
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}size_t size(){return _finish - _start;}size_t capacity(){return _end_of_storage - _start;}//可读可写T& operator[](size_t i){assert(i < size());return _start[i];}//只读const T& operator[](size_t i) const{assert(i < size());return _start[i];}bool empty(){return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
}
2. 核心接口
2.1 增删与扩容
增删接口主要讲解的有尾删尾删、插入删除以及扩容
2.1.1 push_back & pop_back
尾插尾删
这里的尾插首先要判断是否需要扩容,然后在数据尾部插入数据即可
尾删则只需要保证所给的位置不超出范围后直接 --_finish 即可
//尾插
void push_back(const T& x)
{if (finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*finish = x;finish++;
}
//尾删
void pop_back()
{assert(!empty());--_finish;
}
2.1.2 insert
在指定位置插入数据
这里首先判断是否需要扩容,然后将指定位置pos之后的数据均向后移动,最后插入数据即可,这里需要注意的是在扩容后原来的pos位置就会失效,也就是迭代器失效,这里就相当于成为了野指针,所以解决方法是将原来的位置记录下来在扩容后更新即可
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_stroage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (pos != end){*(end + 1) = *end;--end;}++_finish;
}
2.1.3 erase
删除指定位置数据
这里删除就是保证所给位置在范围内然后直接将要删除的位置之后的数据覆盖到原来位置即可,需要注意的是在erase操作后也会出现迭代器失效,所以要继续使用pos的话需要记录原来的位置然后更新
//erase后的迭代器也看做是失效的
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()){*(it + 1) = *it;it++;}_finish--;
}
2.1.4 resize
初始化指定长度的指定数据
这里主要注意的是要分三种情况:1. n < size(),这时就直接缩容即可;2. size() < n < capacity(),这里就直接插入数据,不用扩容;3. capacity() < n,这里需要扩容后插入数据
//这里的T可以是string类也可以是内置类型int等,string类有自己的默认构造函数
//内置类型同样有自己的默认构造函数
void resize(size_t n, T val = T())
{if (n < size())//n小于_size则缩容{_finish = _start + n;}else{reserve(n);//如果n超过_capacity则扩容while (_finish != _start + n){*_finish = val;++_finish;}}
}
2.1.5 reserve
判断空间是否充足后进行扩容
扩容操作就是判断原容量是否足够插入新数据,不够则二倍扩容即可,这里的注意事项有:1. 要将原来的size()保存成为old_size,以保证之后的_finish在计算时不会出现野指针的情况,因为如果不保存原来的size(),那么在之后size()就会更新成为新的size(),这里计算就会出现差错
2. 注意memcpy是浅拷贝,在处理内置类型int、double这样的数据不会出错,但是如果是string或者vector这样需要深拷贝类型的数据就会出错导致内存泄漏,解决方法就是使用其本身的赋值运算,这样可以兼顾二者
//扩容
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));//拷贝数据for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;//释放旧空间_start = tmp;//指向新空间_finish = tmp + old_size;//tmp(新的) + _finish(原来) - _start(原来)_end_of_storage = tmp + n;}
}
2.2 拷贝构造
2.2.1 构造函数与析构函数
这里涉及的知识点有:
1. 类模版的成员函数仍然可以是函数模版
2. 在迭代器区间构造的函数时可以使用函数模版,这样可以使用任意容器的迭代器初始化,例如链表
//直接走初始化列表,如果没有则使用缺省值
vector()
{}
//C++11强制生成默认构造
//vector() = default;//类模版的成员函数仍然可以是函数模版
//迭代器区间构造
//可以使用任意容器的迭代器初始化
//例如链表
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}//使用n个val初始化
vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){ push_back(val);}
}~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage;}
}
2.2.2 拷贝构造函数
这里直接只用范围for进行深拷贝操作,简单便捷,并且提前保存空间减少扩容带来的消耗
//拷贝构造
vector(const vector<T>& v)
{reserve(v.size());for (auto& e : v){//这里默认有一个this指针//直接完成了深拷贝的工作push_back(e);}
}
2.3 赋值重载
这里涉及了两种写法:
1. 自己完成赋值:人为进行赋值的操作,过程比较清晰
2. 交给编译器完成赋值:交给编译器直接完成交换以达到赋值的目的,操作更加简便
//赋值重载
1、传统写法
void clear()
{_finish = _start;
}vector<T>& operator=(const vector<T>& v)//传地址
{if (this != &v){clear();reserve(v.size());for (auto& e : v){push_back(e);}}return *this;
}//2、现代写法
void swap(const vector<T> v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
vector<T>& operator=(const vector<T> v)//传值
{swap(v);return *this;
}
3. 常见问题
3.1 迭代器失效
这里的迭代器失效有两种情况:
1. 扩容后迭代器失效:这里首先了解扩容的原理是开辟一个原来空间二倍的空间,将原空间数据拷贝到扩容后的空间,然后释放原空间,所以由于pos在扩容后仍然指向的是原空间,而原空间已经释放,所以导致了类似野指针的效果,也就是第一种迭代器失效
2. 未扩容但是挪动数据导致的迭代器失效:即在使用原来pos完成挪动数据的操作后,pos已经不指向原来的位置,这时如果仍然想要访问原来位置就会导致迭代器失效
解决方法:保存原来的迭代器指向的位置或者相对位置,然后在扩容或者挪动数据之后更新pos指向的位置即可
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);//1.扩容,注意避免迭代器失效,即pos在扩容后仍然指向之前的空间//在之后对新空间的操作时会失效,类似野指针,就是迭代器失效//这里使用相对位置来避免迭代器失效//还需要注意,在insert之后的迭代器是失效的,不可以直接继续访问,但是可以在更新后访问//2.不扩容,但是由于数据的挪动导致p不再指向原来的位置,也看做迭代器失效//关于迭代器失效的处理方法就是接收操作之后的迭代器,然后对更新后的迭代器进行操作即可if (_finish == _end_of_stroage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (pos != end){*(end + 1) = *end;--end;}++_finish;
}
//erase后的迭代器也看做是失效的
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()){*(it + 1) = *it;it++;}_finish--;
}
3.2 浅拷贝
浅拷贝就是指两个指针指向同一块空间,这时如果一个指针对该空间进行释放或者其他操作会影响另一个指针导致内存泄漏等后果,解决方法很简单就是深拷贝使两指针指向不同的空间然后使其数据保持一致即可
//扩容
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));//拷贝数据//这里的T如果是string或者vector类型这种需要深拷贝的类型// 则需要赋值进行拷贝数据,否则浅拷贝会导致内存泄漏//这里调用的赋值就是string/vector的赋值,就是深拷贝for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;//释放旧空间_start = tmp;//指向新空间_finish = tmp + old_size;//tmp(新的) + _finish(原来) - _start(原来)_end_of_storage = tmp + n;}
}
3.3 类型转换
这个问题是因为由于给出的代码编译器不知道该代码是类型还是变量,所以导致识别不出来,解决方法就是改为 auto 自动转换或者前面加上 typename 来告诉编译器即可
template<class T>
void print_vector(const vector<T>& v)
{//编译器无法确定此处的const_iterator是类型还是静态成员变量//所以规定不能从未实例化的类模版内取东西,需要程序员自己规定//即加一个 typename 来表示这里的const_iterator是类型即可typename vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;
}
相关文章:
深度学习 vector 之模拟实现 vector (C++)
1. 基础框架 这里我们有三个私有变量,使用 _finish - _start 代表 _size,_end_of_storage - _start 代表 _capacity,并且使用到了模版,可以灵活定义存储不同类型的 vector,这里将代码量较小的函数直接定义在类的内部使…...
关于LLC知识10
在LLC谐振腔中能够变化的量 1、输入电压 2、Rac(负载) 所以增益曲线为红色(Rac无穷大)已经是工作的最大极限了,LLC不可能工作在红色曲线之外 负载越重时,增益曲线越往里面 假设: 输入电压…...
最长的严格递增或递减子数组
给你一个整数数组 nums 。 返回数组 nums 中 严格递增 或 严格递减 的最长非空子数组的长度。 示例 1: 输入:nums [1,4,3,3,2] 输出:2 解释: nums 中严格递增的子数组有[1]、[2]、[3]、[3]、[4] 以及 [1,4] 。 nums 中…...
【JavaEE】SpringBoot 统一功能处理:拦截器、统一数据返回与异常处理的综合应用与源码解析
目录 SpringBoot 统⼀功能处理拦截器拦截器快速⼊⻔拦截器详解拦截路径拦截器执⾏流程 登录校验定义拦截器注册配置拦截器 DispatcherServlet 源码分析(了解)初始化(了解) DispatcherServlet的初始化1. HttpServletBean.init()2. FrameworkServlet.initServletBean() WebApplic…...
I2C学习:上拉电阻选取
一.I2C简介 I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。I2C总线在使用时,需要接上拉电阻,这是因为I2C接口是开漏输出,如图1所示。 图1 I2C开漏输出 I2C有5种速度模式:标准(100KHz&am…...
AC自动机-1
AC自动机(Aho-Corasick Automaton)是一种高效的多模式字符串匹配算法。它是由Alfred Aho和Margaret Corasick在1975年提出的。这种算法可以在一次扫描输入文本的情况下,同时查找多个模式串。 基本概念 Trie树 AC自动机是基于字典树数据结构构建的字典树…...
注解@Service@Component@Slf4j@Data
在Java中,这四个注解分别属于不同的用途和库,下面是它们各自的作用: Service: 这个注解通常用于Spring框架中,它用于标记服务层组件。在Spring中,服务层通常包含业务逻辑。当一个类被标记为Service…...
【Nodejs】六、express框架
目录 一、express 介绍 二、express 使用 2.1 express 下载 2.2 express 使用 三、express 路由 3.1 什么是路由 3.2 路由的使用 3.3 获取请求参数 3.4 获取路由参数 四、express 响应设置 五、express 中间件 5.1 什么是中间件 5.2 中间件的作用 5.3 中间件的类…...
进阶 pro max
最近搞了许多有趣的东西,比如自制rtos,速成数模电,学了一点点的AD,看着视频弄了HAL库,以及定时器和串口中断配合实现接收任意长度(不超过缓冲值)数据,还有配置hal库的freertosfafts …...
Agentic Security:一款针对LLM模型的模糊测试与安全检测工具
关于Agentic Security Agentic Security是一款针对LLM模型的模糊测试与安全检测工具,该工具可以帮助广大研究人员针对任意LLM执行全面的安全分析与测试。 请注意 Agentic Security 是作为安全扫描工具设计的,而不是万无一失的解决方案。它无法保证完全防…...
Spring Cloud Config 与 Spring Cloud Bus 来实现动态配置文件
要使用 Spring Cloud Config 与 Spring Cloud Bus 来实现动态配置文件,你可以按照以下步骤操作: ### 步骤 1: 添加依赖 首先,确保你的项目中添加了 Spring Cloud Config 客户端和 Bus 的依赖。对于 Maven 项目,pom.xml 文件应该…...
Qt:Qt背景
目录 1.Qt解释 2.Windows下开发GUI的方案 3.框架 4.Qt历史 4.Qt支持的平台 5.Qt版本 6.Qt案例 1.Qt解释 前端开发,分为网页前端开发(Web)、桌面应用开发(Windows、Linux)、移动应用开发(Android)。Q…...
【数据结构】选择排序
🍬个人主页:Yanni.— 🌈数据结构:Data Structure. 🎂C语言笔记:C Language Notes 🏀OJ题分享: Topic Sharing 目录 前言: 基本思想 直接选择排序 思路分…...
国产GD32单片机开发入门(二)GD32单片机详解
文章目录 一.概要二.单片机型号命名规则三.GD32F103系统架构四.GD32F103C8T6单片机启动流程五.GD32F103C8T6单片机主要外设资源六.单片机开发过程中查看芯片数据手册的必要性1.单片机外设资源情况2.GD32单片机内部框图3.GD32单片机管脚图4.GD32单片机每个管脚功能5.单片机功耗数…...
8个我平时每天都会看的网站,涵盖办公、娱乐、学习等
分享8个我平时每天都会看的网站,涵盖办公、娱乐、学习等多种类别,试过就知道有多好用! 1、MyFreeMP3 tools.liumingye.cn/music/#/ 一个可以免费听歌的平台,不用充会员,里面收录了大多数的国内外知名流行歌手、乐队的…...
Vue2——父子之间间的调用
1、父组件给子组件传值使用props 父组件: <div><SonPage msg"通过props传递值---父>子" ></SonPage><h1>父组件</h1></div> 子组件 <div :style"{border: 1px solid red}"><h1>子组件…...
xfs Vs ext4?
xfs测试 ext4 测试 对比 XFS和EXT4都是Linux系统中广泛使用的文件系统,它们各有特点和优势,选择哪一个取决于你的具体需求和使用场景。下面是它们的主要特点: XFS: 由Silicon Graphics Inc.开发,最初用于SGI的IRIX系统。支持非…...
数据结构stack (笔记)
文章目录 1. 概念理解易混淆内容 2. 时间复杂度3. 实现方式4. 应用5. 内容出处 1. 概念理解 stack(中文名:堆栈、栈):虽然它叫堆栈,但是它其实指的是栈,跟堆没啥关系。 栈的特性:先进后出、后进先出(这个过程就…...
SQL - 创建 表和数据库
创建和删除数据库 create database if not exists sql_store2; //创建 drop database if exists sql_store2; //删除 -- 创建数据库 create database if not exists sql_store2; drop database if exists sql_store2; 创建表 create table customers (someting); -- 创建表 cre…...
使用 Arch Linux 几个月有感 | 为什么我选择 Arch Linux ,Arch 的优缺点有什么 | 一些Linux发行版推荐
(终端是 Yakuake ,KDE 自带) 一点碎碎念,可以跳过不看 几年前从 CentOS 接触的 Linux ,试图搭建一个KMS服务器 但是失败了 ,后来装过 Ubuntu Debian deepin Kali Kubuntu Manjaro,踩一路坑最后…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
