【C++】vector模拟实现

🔥个人主页: Forcible Bug Maker
🔥专栏: STL || C++
目录
- 前言
- 🔥vector需要实现的接口函数
- 🔥vector的模拟实现
- ==swap交换==
- ==默认成员函数==
- ==迭代器接口==
- ==reserve和resize==
- ==size和capacity==
- ==operator[ ]下标获取==
- ==push_back和pop_back==
- ==插入(insert)和删除(erase)==
- 结语
前言
本篇博客主要内容:STL库中vector的模拟实现。
在之前完成string以及学习了vector一些接口函数的基础上,这个vector的实现相当于是一个奖励内容,并不困难。不过我们这里vector底层实现和上次string的有所不同,是通过三个指针_start,_finish和_end_of_storage来维护这个模板类的。相信在看完今天vector的实现之后,能对C++的迭代器有更深的了解。
🔥vector需要实现的接口函数
由于涉及到了模板的内容,我们这次不会将vector实现的声明和定义分离。在后期模板进阶的阶段会进一步解答此问题,盲目将模板类的声明定义分离是很容易出错的(在对模板这部分内容不熟练的情况下)。
看看需要实现的接口函数:
#pragma once
#include<iostream>
#include<algorithm>
#include<cassert>
using namespace std;
namespace ForcibleBugMaker
{template<class T>class vector{public:// 这里vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;//迭代器获取接口iterator begin();iterator end()const_iterator cbegin()const;const_iterator cend() const;// 交换vector对象void swap(vector<T>& v);// 构造函数,析构函数以及赋值运算符重载vector();vector(int n, const T& value = T());template<class InputIterator>vector(InputIterator first, InputIterator last);vector(const vector<T>& v);vector<T>& operator=(vector<T> v);~vector();// 容量获取和调整接口size_t size() const;size_t capacity() const;void reserve(size_t n);void resize(size_t n, const T& value = T());// 元素获取T& operator[](size_t pos);const T& operator[](size_t pos)const;// 插入和删除元素void push_back(const T& x);void pop_back();iterator insert(iterator pos, const T& x);iterator erase(iterator pos);private:iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _end_of_storage = nullptr; // 指向存储容量的尾};
}
本篇建议在string类实现的基础上进行,不然有些概念可能不太好理解。C++中包含交换函数swap,在命名空间std中,可以直接使用。在vector的实现中,你可能发现与string不同,接口函数大多使用迭代器(指针)来实现,传入和传出的参数基本上也都是迭代器。这么做是为了给后面一系列的STL容器打一个基础,大家要逐渐习惯这样的实现方式。
🔥vector的模拟实现
接下来进入主要内容,按照上面的接口列表逐一实现。
本篇都是直接在vector模板类内部实现,不用手动圈定命名空间。
swap交换
既然是用三个指针维护的,那么交换这三个指针即可完成交换。
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
默认成员函数
构造函数:
这里我们提供了三种重载,分别是:
// 无参构造
vector()
{}// 构造包含n个value的vector对象
vector(int n, const T& value = T())
{reserve(n);for (int i = 0; i < n; i++) {this->push_back(value);}
}// 通过迭代器区间构造vector对象
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{InputIterator it = first;while (it != last) {this->push_back(*it);++it;}
}
学习过vector的使用,应该也能看懂。其中有几个还待实现的接口函数,如push_back,reserve等。
对于第一个无参构造,其实编译器默认实现的也有相同的效果,但是一旦你实现了下面两个构造,那么编译器就不会生成默认的了。C++提供了一种语法,可以无视你的重载构造,强制生成一个编译器默认构造,所以,下面这样写也是正确的的。
// 强制编译器生成默认无参构造vector() = default;vector(int n, const T& value = T()){reserve(n);for (int i = 0; i < n; i++) {this->push_back(value);}}template<class InputIterator>vector(InputIterator first, InputIterator last){InputIterator it = first;while (it != last) {this->push_back(*it);++it;}}
第二个构造中缺省参数T()表示的是构造一个T类型的对象,赋值给value。在C++中不但T()可以用于表示自定义类型变量的无参构造,也同样可以表示内置类型的无参构造(C语言中并不支持这样做)。
如下:
int a = int();
int b = int(3);
cout << a << endl;
cout << b << endl;

对于第三个,迭代器构造,实现的是一个模板成员函数,支持各种类型迭代器的区间构造。
析构函数:
~vector()
{delete[] _start;_start = _finish = _end_of_storage = nullptr;
}
拷贝构造:
vector(const vector<T>& v)
{reserve(v.capacity());int size = v.size();for (int i = 0; i < size; i++) {this->push_back(v[i]);}
}
赋值运算符重载:
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}
这是一种比较新的实现方式,在string中也有使用过。
迭代器接口
本篇vector虽然使用指针实现了迭代器,但并不是说只有这一种实现方式。你也可以将迭代器定义成一个模板类,具体内容可以在不久后的list篇章学到。
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;
}
reserve和resize
reserve开辟容量空间capacity,但不改变vector对象原本的size区间中的内容。
void reserve(size_t n)
{if (n > capacity()) {T* tmp = new T[n];size_t pos = size();for (int i = 0; i < size(); i++) {tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + pos;_end_of_storage = _start + n;}
}
resize也可以用作开辟空间,同时会改变size的值。
void resize(size_t n, const T& value = T())
{if (n > capacity()) {reserve(n);}while (_finish < _start + n) {*_finish = value;++_finish;}_finish = _start + n;
}
size和capacity
获取vector对象的size和capacity,通过指针相减就可以得到。
size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}
operator[ ]下标获取
通过运算符重载的operator[]获取vector对象的元素。
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos)const
{assert(pos < size());return _start[pos];
}
同时重载了非const版本和const版本。
push_back和pop_back
push_back和pop_back分别是尾插一个元素和尾删一个元素。
复用之前的reserve接口函数push_back功能其实并不难实现。
void push_back(const T& x)
{if (_finish == _end_of_storage) {reserve(size() == 0 ? 4 : size() * 2);}*_finish = x;++_finish;
}void pop_back()
{assert(size() > 0);--_finish;
}
插入(insert)和删除(erase)
这两个函数需要传入的参数都为迭代器,从vector中间部分插入和删除元素。
插入:会将我们传入的元素插入到迭代器指向的对象之前;如果迭代器指向end(),则功能等同于尾插。
删除:将传入迭代器指向的元素删除。
iterator insert(iterator pos, const T& x)
{assert(_start <= pos && pos <= _finish);if (_finish == _end_of_storage) {int gap = pos - _start;reserve(size() == 0 ? 4 : size() * 2);pos = _start + gap;}iterator it = _finish;while (it > pos) {*it = *(it - 1);--it;}*it = x;++_finish;return it + 1;
}iterator erase(iterator pos)
{assert(_start <= pos && pos < _finish);int size = pos - _start;iterator itCur = pos + 1;while (itCur != _finish) {*(itCur - 1) = *itCur;++itCur;}--_finish;return _start + size;
}
注:vector的insert和erase可能导致大量数据的移动,开销较大,所以非必要情况少用。
结语
本篇博客主要介绍了vector常用接口的实现,包括迭代器以及迭代器接口,元素的插入和删除,以及各种默认成员函数的实现。
后续博主还会继续分享与STL相关的内容,感谢大家的支持。♥
相关文章:
【C++】vector模拟实现
🔥个人主页: Forcible Bug Maker 🔥专栏: STL || C 目录 前言🔥vector需要实现的接口函数🔥vector的模拟实现swap交换默认成员函数迭代器接口reserve和resizesize和capacityoperator[ ]下标获取push_back和…...
生成随机图片
package com.zhuguohui.app.lib.tools;/*** Created by zhuguohui* Date: 2024/6/1* Time: 13:39* Desc:获取随机图片*/ public class RandomImage {// static final String url "https://picsum.photos/%d/%d?random%d";static final String url "https://…...
回溯算法常见思路
回溯问题 回溯法,一般可以解决如下几种问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数…...
AR眼镜定制开发_在AR眼镜中实现ChatGPT功能
AR眼镜定制方案中,需要考虑到强大的算力、轻巧的设计和更长的续航时间等基本要求。然而,AR眼镜的设计方案不仅仅需要在硬件和显示技术方面取得突破,还要在用户体验方面有所进展。 过去,由于造价较高,AR眼镜的普及和商业…...
手写防抖debounce
手写防抖debounce 应用场景 当需要在事件频繁触发时,只执行最后一次操作,可以使用防抖函数来控制函数的执行频率,比如窗口resize事件和输入框input事件; 这段代码定义了一个名为 debounce 的函数,它接收两个参数:fn…...
anaconda pycharm jupter分别是
Anaconda Anaconda是一个面向数据科学的Python发行版,它包含了Python解释器、conda包管理器、以及大量的科学计算和数据分析库。Anaconda的主要功能是提供一个易于管理的环境,用于安装、运行和更新Python包,同时支持创建和切换不同的Python环…...
【JMeter接口自动化】第3讲 Jmeter语言及外观配置
Jmeter语言配置 方法一:暂时生效,下次打开JMeter还会恢复默认配置 Jmeter安装后,默认语言是英文,可以在“选项”——“选择语音”中更改 方法二,修改配置文件,永久生效 修改jmeter.properties文件 Jmete…...
浅谈云原生安全
一、云原生安全的层级概念 "4C" Code-Container-Cluster-Cloud 二、云原生各个层级的安全实践有哪些? 1、针对于Cloud针对的是公有云层面,其实就一点 1、使用主账号子角色,赋予最小权限原则进行资源管理。 2、对于Cluster 1、从C…...
[线程与网络] 网络编程与通信原理(五): 深入理解网络层IP协议与数据链路层以太网协议
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 🧀Java …...
【Python】超时请求或计算的处理
超时机制 一般应用于处理阻塞问题 场景: 复杂度较大的计算(解析)某个数值、加解密计算等请求中遇到阻塞,避免长时间等待网络波动,避免长时间请求,浪费时间 1. requests 请求超时机制 reqeusts 依赖中的…...
前端 JS 经典:LRU 缓存算法
前言:什么是 LRU 呢,单词全拼 Least Recently Used,意思是最久未使用。这个算法是做缓存用的,比如,你要缓存一组数据,你要划分缓存块出来,因为不可能每个数据都做缓存,那么划出来的这…...
Python3 笔记:ljust、rjust 和 center
1、ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。 ljust(self, width, fillchar , /) width -- 指定字符串长度。 fillchar -- 填充字符,默认为空格。 str1 sentence str2 word…...
恒压频比开环控制系统Matlab/Simulink仿真分析(SPWM控制方式)
介绍恒压频比的开环控制方法驱动永磁同步电机的转动,首先分析恒压频比的控制原理,然后在Matlab/Simulink中进行永磁同步电机恒压频比开环控制系统的仿真分析,最后将Simulink中的恒压频比控制算法生成代码加载到实际工程中进行工程实现。 一、…...
chatgpt4和文心一言的简单对比
功能1. 写一个ebpf程序监控udp延迟 文心一言 答案 编写一个完整的eBPF程序来监控UDP延迟涉及多个步骤,包括编写eBPF C代码、编译、加载到内核,以及用户空间程序来读取eBPF map中的数据。以下是一个简化的示例框架,展示如何使用eBPF来监控U…...
React 为什么使用map来渲染列表 而不是其他循环方法
1. 声明式与函数式编程 React强调声明式编程,这意味着你只需要关心代码“做什么”,而不是“怎么做”。.map()函数是一种高阶函数,它属于函数式编程范畴,能够返回一个新数组,这非常适合用于生成组件列表。 使用.map()…...
【Axure高保真】tab切换输入表单
今天和大家分享tab切换输入表单的原型模板,这个模板方便我们快速制作表单,里面包含了输入框、下拉列表、选择器共10多种常用的元件,后续也可以根据需要自行添加到中继器里。点击tab标签可以分类填写对应的内容,这个原型模板是用中…...
OrangePi AI Pro 测试体验
感谢CSDN活动提供的OrangePi AI Pro ,之前一直用的树莓派,正好体验一下新的国产设备, 1、开机体验 整个设备包装不错,链接键盘、屏幕和鼠标,整体开机体验不错,内置OS不错,这个系统内嵌了中文输…...
【C++】:模板初阶和STL简介
目录 一,泛型编程二,函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 三,类模板3.1 类模板的定义格式3.2 类模板的实例化 四,STL简介(了解)4.1 什…...
【软件开发】Java学习路线
本路径视频教程均来自尚硅谷B站视频,Java学习课程我已经收藏在一个文件夹下,B站文件夹同时会收藏其他Java视频,感谢关注。指路:https://www.bilibili.com/medialist/detail/ml3113981545 2024Java学习路线(快速版&…...
git拉去代码报错“Failed to connect to 127.0.0.1 port 31181: Connection refused“
最近参与了一个新项目,在使用git clone 克隆代码时遇到了一个报错"fatal: unable to access ‘https://example.git/’: Failed to connect to 127.0.0.1 port 31181: Connection refused",今天就和大家分享下解决过程。 报错详情 在使用git clone 克隆…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
