深度学习 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,踩一路坑最后…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
