(C++ STL) 详解vector模拟实现
目录
一.vector的介绍
1.vector的介绍
二.vector的定义模拟实现
三.vector各接口的模拟实现
1.vector迭代器的模拟实现
2.构造函数
2.1无参构造
2.2 n个val构造
2.3迭代器区间构造
2.4通过对象初始化(拷贝构造)
3.析构函数
4.size
5.operator=
6.capacity
7.reserve
8.resize
9.operator[ ]
10.insert
11.push_back
12.erase
13.pop_back
14.empty
一.vector的介绍
1.vector的介绍
这是官方的文档介绍
cplusplus.com/reference/vector/vector/
1. vector是表示可变大小数组的序列容器。
2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好
二.vector的定义模拟实现
首先我们先 定义一个命名空间 来模拟实现咱们的vector类
类里面有三个私有 指针变量 分别指向数据块的开始,尾和存储容量的尾
namespace zyl
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _endOfStorage; // 指向存储容量的尾};
}
三.vector各接口的模拟实现
1.vector迭代器的模拟实现
vector的迭代器分为俩种
一种是普通迭代器 指向的内容可以被修改
一种是const迭代器 不可以修改只可读
iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}
2.构造函数
vector 的四种构造函数
2.1无参构造
主要是对各个指针初始化 赋值为空
vector():_endOfStorage(nullptr),_start(nullptr),_finish(nullptr){}
2.2 n个val构造
直接向数组中尾插数据,用reserve提前扩容, 提高效率
然后需要注意的是 这里传参的第二个参数使用匿名对象,const T& val = T() 这种写法会调用默认构造(可以是任意类型),我们前面讲内置类型是没有默认构造函数的, 理论而言是没有的, 但是调用模板之后必须要支持默认构造
vector(int n, const T& value = T()):_endOfStorage(nullptr), _start(nullptr), _finish(nullptr){reserve(n);//提前开n个空间for (int i = 0;i < n;i++){push_back(value);//缺省值默认为val;}}
2.3迭代器区间构造
这里又要使用模板实现, 要实现一个任意类型的迭代器允许任意类型的数据使用,直接用迭代器遍历数组, 尾插数据即可
template<class InputIterator>vector(InputIterator first, InputIterator last):_endOfStorage(nullptr),_start(nullptr),_finish(nullptr){while (first != last){push_back(*first);++first;}
2.4通过对象初始化(拷贝构造)
通过传一个vector对象 然后进行交换
vector(const vector<T>& v){vector<T> tmp(v.cbegin(), v.cend());swap(tmp);}
3.析构函数
析构函数的主要功能 释放掉所有数据 然后三个指针指向空
~vector(){delete[]_start;_start = nullptr;_finish = nullptr;_endOfStorage = nullptr;}
4.size
返回当前vector长度
size_t size() const{return _finish - _start;}
5.operator=
运算符重载= 实现深拷贝 把v对象赋给this
vector<T>& operator= (vector<T> v){if (this != &v){delete[] _start;_start = new T[v.capacity()];for (size_t i = 0;i < v.size();i++){_start[i] = v[i];}_finish = _start + v.size();_endOfStorage = _start + v.capacity();}return *this;}
6.capacity
返回当前vector对象的容量是多少
size_t capacity() const{return _endOfStorage - _start;}
7.reserve
在n>capacity时去进行扩容 是为了防止程序缩容
判段当前数据是为为空,需不需要旧数据的拷贝转移
遍历的时候,一定要使用深拷贝,不要使用memcpy去进行拷贝
void reserve(size_t n){if (n >capacity()){size_t sz = size();T* tmp = new T[n];if (_start)//如果为空 则不用将旧数据转移{for (size_t i = 0;i <size();i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endOfStorage = _start + n;}}
8.resize
n < size()
就是删除数据,直接改变 _finish的指向即可
n > capacity()
调用reserve函数扩容, 后遍历给数组赋值
void resize(size_t n, const T& value = T()){//查看是否需要扩容if (n > capacity()){reserve(n);}if (n > size()){while (_finish > _start + n){*_finish = value;++_finish;}}else{_finish = _start + n;}}
9.operator[ ]
vector也支持下标访问
重载 [ ] 可以快速的对数据进行访问
T& operator[](size_t pos){assert(pos < size());return _start[pos];}
10.insert
检查容量,观察是否需要扩容, 扩容前计算出pos与start之间,pos与start之间相对距离不变,扩容后更新pos位置(这里存在迭代器失效的问题)
遍历挪动数据
将val插入pos位置
注意: 检查pos位置的合法性
iterator insert(iterator pos, const T& x){//pos范围必须在_start和_finish之间assert(pos>=_start);assert(pos <= _finish);//内存满了 进行扩容if (_finish == _endOfStorage){size_t len = pos - _start;reseve(capacity() > 0 ? 4 : capacity * 2);pos = _start + len;}iterator end = _finish;//移动数据 进行插入while (end >= pos){*end = *(end - 1);end--;}*pos = x;++_finish;return pos;}
11.push_back
尾插 直接调用insert 在_finish位置去插入
void push_back(const T& x){insert(_finish, x);}
12.erase
erase函数可以删除所给迭代器pos位置的数据
在删除数据前需要判断容器释放为空
若为空则需做断言处理,删除数据时直接将pos位置之后的数据统一向前挪动一位,将pos位置的数据覆盖即可。
iterator erase(iterator pos){//判断pos是否合法assert(pos > _finish);assert(pos < _start);assert(!empty());iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}
13.pop_back
pop_back直接调用erase去_finish位置进行删除
void pop_back(){erase(_finish);}
14.empty
进行判空 直接看_finish == _start是否相同就可以了
bool empty() const{return _finish == _start;}
相关文章:

(C++ STL) 详解vector模拟实现
目录 一.vector的介绍 1.vector的介绍 二.vector的定义模拟实现 三.vector各接口的模拟实现 1.vector迭代器的模拟实现 2.构造函数 2.1无参构造 2.2 n个val构造 2.3迭代器区间构造 2.4通过对象初始化(拷贝构造) 3.析构函数 4.size 5.operato…...

c语言从入门到实战——C语言数据类型和变量
C语言数据类型和变量 前言1. 数据类型介绍1.1 字符型1.2 整型1.3 浮点型1.4 布尔类型1.5 各种数据类型的长度1.5.1 sizeof操作符1.5.2 数据类型长度1.5.3 sizeof中表达式不计算 2. signed 和 unsigned3. 数据类型的取值范围4. 变量4.1 变量的创建4.2 变量的分类 5. 算术操作符&…...

[论文精读]Semi-Supervised Classification with Graph Convolutional Networks
论文原文:[1609.02907] Semi-Supervised Classification with Graph Convolutional Networks (arxiv.org) 论文代码:GitHub - tkipf/gcn: Implementation of Graph Convolutional Networks in TensorFlow 英文是纯手打的!论文原文的summari…...
CICD:使用docker+ jenkins + gitlab搭建cicd服务
持续集成解决什么问题 提高软件质量效率迭代便捷部署快速交付、便于管理 持续集成(CI) 集成,就是一些孤立的事物或元素通过某种方式集中在一起,产生联系,从而构建一个有机整体的过程。 持续,就是指长期…...

新能源电池试验中准确模拟高空环境大气压力的解决方案
摘要:针对目前新能源电池热失控和特性研究以及生产中缺乏变环境压力准确模拟装置、错误控制方法造成环境压力控制极不稳定以及氢燃料电池中氢气所带来的易燃易爆问题,本文提出了相应的解决方案。方案的关键一是采用了低漏率电控针阀作为下游控制调节阀实…...
Python 中的模糊字符串匹配
文章目录 Python中使用thefuzz模块匹配模糊字符串使用process模块高效地使用模糊字符串匹配今天,我们将学习如何使用 thefuzz 库,它允许我们在 python 中进行模糊字符串匹配。 此外,我们将学习如何使用 process 模块,该模块允许我们借助模糊字符串逻辑有效地匹配或提取字符…...
记录一个奇怪bug
一开始Weapon脚本是继承Monobehavior的,实例化后挂在gameObject上跟着角色。后来改成了不继承mono的,也不实例化。过程都是顺利的,运行也没问题,脚本编辑器也没有错误。 但偶尔有一次报了一些错误,大概是说Weapon (1)…...

SpringBoot面试题7:SpringBoot支持什么前端模板?
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:SpringBoot支持什么前端模板? Spring Boot支持多种前端模板,其中包括以下几种常用的: Thymeleaf:Thymeleaf是一种服务器端Java模板引擎,能够…...
leetcode做题笔记172. 阶乘后的零
给定一个整数 n ,返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 示例 1: 输入:n 3 输出:0 解释:3! 6 ,不含尾随 0示例 2: 输入:n 5 输出&a…...
linux之shell脚本练习
以下脚本已经是在ubuntu下测试的 demo持续更新中。。。 1、for 循环测试,,,Ping 局域网 #!/bin/bashi1 for i in {1..254} do# 每隔0.3s Ping 一次,每次超时时间3s,Ping的结果直接废弃ping-w 3 -i 0.3 192.168.110.$i…...

CSS阶详细解析一
CSS进阶 目标:掌握复合选择器作用和写法;使用background属性添加背景效果 01-复合选择器 定义:由两个或多个基础选择器,通过不同的方式组合而成。 作用:更准确、更高效的选择目标元素(标签)。…...

osWorkflow-1——osWorkflow官方例子部署启动运行(版本:OSWorkflow-2.8.0)
osWorkflow-1——osWorkflow官方例子部署启动运行(版本:OSWorkflow-2.8.0) 1. 前言——准备工作1.1 下载相关资料1.2 安装翻译插件 2. 开始搞项目2.1 解压 .zip文件2.2 简单小测(war包放入tomcat)2.3 导入项目到 IDE、…...

Stm32_标准库_13_串口蓝牙模块_手机与蓝牙模块通信
代码: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h"char News[100] "";uint8_t flag 1;void Get_Hc05News(char *a){uint32_t i 0…...

Unity中用序列化和反序列化来保存游戏进度
[System.Serializable]标记类 序列化 [System.Serializable]是一个C#语言中的属性,用于标记类,表示该类的实例可以被序列化和反序列化。序列化是指将对象转换为字节流的过程,以便可以将其保存到文件、数据库或通过网络传输。反序列化则是将字…...
Junit 单元测试之错误和异常处理
错误和异常处理是测试中非常重要的部分。假设我们有一个服务,该服务从数据库中获取用户。现在,我们要考虑的错误场景是:数据库连接断开。 整体代码示例 首先,为了简化,我们让服务层就是简单的类,然后使用I…...

LockSupport-park和unpark编码实战
package com.nanjing.gulimall.zhouyimo.test;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;/*** author zhou* version 1.0* date 2023/10/16 9:11 下午*/ public class LockSupportDemo {public static void main(String[] args) {…...

js深拷贝与浅拷贝
1.浅拷贝概念 浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。 特性: 会新创建一个对象,即objobj2返回fasle…...

Docker-harbor私有仓库部署与管理
搭建本地私有仓库 #首先下载 registry 镜像 docker pull registry #在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json { "insecure-registries": ["20.0.0.50:5000"], #添加,注意用逗号结…...

ArcGIS笔记8_测量得到的距离单位不是米?一经度一纬度换算为多少米?
本文目录 前言Step 1 遇到测量结果以度为单位的情况Step 2 简单的笨办法转换为以米为单位Step 3 拓展:一经度一纬度换算为多少米 前言 有时我们会遇到这种情况,想在ArcGIS中使用测量工具测量一下某一段距离,但显示的测量结果却是某某度&…...

SpringBoot入门详解
目录 因何而生的SpringBoot 单体架构的捉襟见肘 SpringBoot的优点 快速入门 高曝光率的Annotation SpringBoot的工作机制 了解SpringBootApplication SpringBootConfiguration EnableAutoConfiguration 自动配置的幕后英雄:SpringFactoriesLoader Compon…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...