C++ vector类模拟实现
目录
一、成员变量
二、构造函数
1.默认构造
2.拷贝构造
3.迭代器构造
4.使用n个值构造
5.赋值拷贝
三、析构函数
四、vector重要成员函数
1.size和capacity函数
2.reserve函数
3.resize函数
4.push_back函数
5.insert函数
6.erase函数
7.重载operator[]
一、成员变量
STL库里面,vector的成员变量和string有一些不一样,他的成员变量是用了迭代器
_start是数组起始位置
_finish是数据内容结束位置的下一个
_endofstorage是开辟的空间结束位置的下一个。
那么vector的迭代器是又是什么呢?
vector使用了模版,他可以适配各种类型,他的迭代器就是这个模板类型的指针。
因为vector和string一样,本质上就是数组,开辟的空间在内存上是连续的,因此使用指针当迭代器是在合理不过了。
二、构造函数
我们模仿一下STL里面的vector,没有使用适配器。
1.默认构造
vector的默认构造很简单,直接写上就好,内容都可以不需要,因为我们在成员变量哪里给到了初始值,他会在初始化列表中自动调用,因此这里可以不需要写初始化列表。
那么我们可以不可以不要下面这局代码呢?
答案是不可以的,因为我们后续还会写上其他的构造函数,那么编译器就不会提供默认生成的构造函数了。那这样我们普通的一个代码 vector<int> v 就会报错
vector()
{}
2.拷贝构造
拷贝构造调用了push_back函数,先开辟好空间,防止后续再扩容,后续尾插即可。
vector(const vector<T>& v)
{reserve(v.capacity());for (auto& e : v){push_back(e);}
}
3.迭代器构造
这里使用了模板来处理,因为我们不仅仅想使用vector迭代器去构造类对象,我们还想使用其他类对象的迭代器去构造。也是利用尾插即可。
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}
4.使用n个值构造
开辟n个空间,并全部赋值为给到的val参数。
vector(int n, const T& val = T())
{resize(n, val);
}vector(size_t n, const T& val = T())
{resize(n,val);
}
这里为什么我们要重载呢,一个写出int类型,一个写成size_t类型?
我们先不写上面的int重载,下面这句代码,使用n个val进行拷贝,竟然报错了,为啥会这样?
48行有问题,我们发现,明明我想调用的是58行的拷贝构造,为啥会到模板那里去?
是因为此时所传的 10 和 1 都是int类型,而下面那个是 size_t和 int 两个类型,编译器认为你要用这个模板来初始化,因此会调用生成 InputIterator为int的函数,int类型去解引用,那可不就报错了嘛,为了让编译器认识两个整形,我们就重载了vector,使他能够知道我们的意思。
5.赋值拷贝
利用了很现代的写法,参数我们不传&,就会拷贝构造一个临时对象,这个临时对象刚好是我this需要的内容,我直接和你swap一下,你身上就有了我不要的内容了,同时出了作用域你会析构,我会引用返回,就完成了赋值拷贝。
vector<T>& operator=(vector<T> v)
{swap(_start,v._start);swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);return *this;
}
三、析构函数
easy,直接delete[] ,顺便置空即可。
~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}
四、vector重要成员函数
1.size和capacity函数
由于我们成员变量都是迭代器(实现方式为指针),因此size()的大小和capacity()都可以用迭代器相减的方式得到。
size_t size() const
{return _finish - _start;
}
size_t capacity() const
{return _endofstorage - _start;
}
2.reserve函数
reserve可以开辟空间,n比现在的空间大才开辟,比现在的空间小则不管,不会删除数据。
如果需要开辟空间,则会先new出新的空间,把原有的数据数据拷贝到这个空间里,再删除原有的数据,同时对成员变量进行修改。
注意我们在拷贝的时候不能使用memcpy,因为用memcpy拷贝,如果类型为自定义类型,就仅仅是浅拷贝,浅拷贝会在开辟新空间的时候进行delete,一旦delete,新空间也被delete了。
这里我们选择了使用循环赋值,因为自定义类型会调用他的赋值拷贝,而他的赋值拷贝一般是深拷贝(只要写了的话),因此我们再扩容的时候就不会因为delete而发生错误了。
void reserve(size_t n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy对自定义类型是浅拷贝//memcpy(tmp, _start, sz * sizeof(T));//循环赋值,会调用自定义类型的赋值拷贝,就是深拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}
}
3.resize函数
resize会改变vector的size()。
如果传的n要比size()小,那么就变成这个长度。
如果n比size()大,就要先看扩不扩容,但是无论扩不扩容,我们都可以直接调用reserve函数,需要扩你就扩,不需要也没啥影响,后面在将你传过来的值,赋值给还未初始化的空间。
注意在C++中一切类型都算对象,包括内置类型,因此我们传参给到的默认参数为T() 就像一个类的默认构造一样。
void resize(size_t n,const T& val = T())
{if (n <= size()){_finish = _start + n;}else{reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}
}
4.push_back函数
push_back函数先判断空间满了没有,满了就去扩容,空间capacity()为0就给4,不为0就给2倍。
然后再*_finish这里赋值,_finish++即可。
void push_back(const T& x)
{if (size() == capacity()){size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = x;_finish++;
}
5.insert函数
insert函数传的参数不是索引,而是迭代器!!!
只要有插入,我们都得判断是否扩容,由于这里传递的是迭代器(vector中是指针),扩容有可能是异地扩容,因此地址会发生改变,我们扩容前先记录一下pos相对于_start的位置,扩容后再加上这个位置赋值给pos才对。
后续就是挪动数据,挪动完赋值,_finish++,最后要返回pos(可以防止迭代器失效)。
iterator insert(iterator pos,const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (size() == capacity()){size_t len = pos - _start; //扩容后_start位置可能会发生改变了,因此要记录pos的相对位置,改变pos的值size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);pos = _start+len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
6.erase函数
erase比insert还要简单一点,也是挪动数据,方向不一样,insert是从后往前数据往后挪动,erase是从当前位置往后向前挪动数据。再_finish--,最后返回pos(这也是为了防止迭代器失效)。
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos;while (it < _finish - 1){*it = *(it +1);++it;}_finish--;return pos;
}
7.重载operator[]
分为普通版本和const版本,普通可读可写,const只能读。
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}
//不可修改
const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}
最后附上总代码
vector.h
#pragma once
namespace kky
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector(){}vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(int n, const T& val = T()){resize(n, val);}//vector(size_t n, const T& val = T())//{// resize(n,val);//}vector<T>& operator=(vector<T> v){swap(_start,v._start);swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy对自定义类型是浅拷贝//memcpy(tmp, _start, sz * sizeof(T));//循环赋值,会调用自定义类型的赋值拷贝,就是深拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n,const T& val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}}void push_back(const T& x){if (size() == capacity()){size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = x;_finish++;}iterator insert(iterator pos,const T& x){assert(pos >= _start);assert(pos <= _finish);if (size() == capacity()){size_t len = pos - _start; //扩容后_start位置可能会发生改变了,因此要记录pos的相对位置,改变pos的值size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);pos = _start+len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos;while (it < _finish - 1){*it = *(it +1);++it;}_finish--;return pos;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}//不可修改const T& operator[](size_t pos) const{assert(pos < size());return _start[pos]; }private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};void test01(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;for (auto e : v){cout << e << " ";}}void test02(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.resize(10, 6);for (auto e : v){cout << e << " ";}cout << endl;v.insert(v.begin(), 30);for (auto e : v){cout << e << " ";}cout << endl;}void test03(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (auto e : v){cout << e << " ";}cout << endl;v.erase(v.begin());for (auto e : v){cout << e << " ";}cout << endl;v.erase(v.begin()+2);for (auto e : v){cout << e << " ";}}void test04(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{it++;}}for (auto e : v){cout << e << " ";}}void test05(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);vector<int> v2(v);for (auto e : v){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;vector<int> v3;v3.push_back(1);v3 = v;for (auto e : v3){cout << e << " ";}}void test06(){vector<int> v(10, 1);for (auto e : v){cout << e << " ";}cout << endl;std::string s("123456");vector<int> v1(s.begin(), s.end());for (auto e : v1){cout << e << " ";}cout << endl;}void test07(){vector<int>v1(10, 1);vector<int>v2(10, 2);v1 = v2;for (auto e : v1){cout << e << " ";}cout << endl;}
}
test.cpp
#include <iostream>
using namespace std;
#include<cassert>
#include"vector.h"
int main()
{kky::test07();}
相关文章:

C++ vector类模拟实现
目录 一、成员变量 二、构造函数 1.默认构造 2.拷贝构造 3.迭代器构造 4.使用n个值构造 5.赋值拷贝 三、析构函数 四、vector重要成员函数 1.size和capacity函数 2.reserve函数 3.resize函数 4.push_back函数 5.insert函数 6.erase函数 7.重载operator[] 一、成…...
FastAPI+Pydantic使用自定义参数校验+自定义异常+全局异常捕获
目录 1 自定义参数校验异常 2 自定义的curr_page_v参数校验函数,如果不合法抛出自定义异常! 3 配置全局异常 1 自定义参数校验异常 # 1.用户自定义异常类型,只要该类继承了Exception类即可 class ValDtoError(Exception):# 初始化def __in…...

Python综合练习题
题目 创建一个系统,里面可以添加学生、添加班级、查看班级里的学生,在控制台输出 效果图 关键代码 完整代码 # -*- coding: UTF-8 -*-#功能 Functionality0 #学生 Student [刘榕榕0, 秦英姿1, 王家乐0, 孟德赫3, 门子伟4, 明展宇5] #班级 Class [大…...

SpringCloud+Nacos集成Seata-1.7.0分布式事务
前言 项目中需要A服务调用B服务,当A服务方法体内出现异常时,若B服务方法已执行,要求B服务能够进行回滚,需要借助分布式事务实现。Seata是一个比较成熟的分布式事务工具,但官方文档比较简洁,查阅网上资料也…...

任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK
任务调度框架 Java中如何实现定时任务? 比如: 1.每天早上6点定时执行 2.每月最后一个工作日,考勤统计 3.每个月25号信用卡还款 4.会员生日祝福 5.每隔3秒,自动提醒 10分钟的超时订单的自动取消,每隔30秒或1分钟查询…...

修炼k8s+flink+hdfs+dlink(六:学习k8s)
一:增(创建)。 直接进行创建。 kubectl run nginx --imagenginx使用yaml清单方式进行创建。 二:删除。 kubectl delete pods/nginx 三:修改。 kubectl exec -it my-nginx – /bin/bash 四:查看。 …...

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-[4]客户端与服务端连接
红队专题 招募六边形战士队员服务端编写新建工程server函数创建主线程类获取配置信息运行command 命令头文件里创建引用win32 类库/头文件startsocket 开始监听 类函数添加类StartSocketmysend/myrecv 设置 m_sockCommon 头文件MSGINFO_S 结构体 ThreadMain头文件runflag 启动 …...
Qt Designer生成ui文件,如何转py文件,如何运行
下面将逐步介绍ui文件如何转py文件,怎么运行的具体操作步骤 ui文件转py文件 1.使用Qt Designer生成ui文件,保存到本地 2.输入 cmd ,打开命令行窗口 3.进入ui文件的目录下,文件路径使用你本地存放ui文件的位置 cd /d ui文件路径…...

Python数据挖掘:自动售货机销售数据分析与应用
📕作者简介:热爱跑步的恒川,致力于C/C、Java、Python等多编程语言,热爱跑步,喜爱音乐的一位博主。 📗本文收录于恒川的日常汇报系列,大家有兴趣的可以看一看 📘相关专栏C语言初阶、C…...

【设计模式】设计模式概述
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...

第六届“中国法研杯”司法人工智能挑战赛进行中!
第六届“中国法研杯”司法人工智能挑战赛 赛题上新! 第六届“中国法研杯”司法人工智能挑战赛(LAIC2023)目前已发布司法大模型数据和服务集成调度 、证据推理、司法大数据征文比赛、案件要素识别四大任务。本届大赛中,“案件要素…...

关于 passing ‘const xx’ as ‘this’ argument of 的错误
今天在写一个简单的函数时,编译时出现了如下的错误: 这个很简单的函数是这样的: struct bundle_set {uint32_t baseId;uint32_t endId;bool operator< (const bundle_set &a){return baseId < a.baseId;} }; 在网上搜索到都是说什…...

数据结构和算法(13):优先级队列
概述 按照事先约定的优先级,可以始终高效查找并访问优先级最高数据项的数据结构,也统称作优先级队列 优先级队列将操作对象限定于当前的全局极值者。 根据数据对象之间相对优先级对其进行访问的方式,与此前的访问方式有着本质区别…...
面试经典150题——Day15
文章目录 一、题目二、题解 一、题目 135. Candy There are n children standing in a line. Each child is assigned a rating value given in the integer array ratings. You are giving candies to these children subjected to the following requirements: Each chil…...

web APIs——第一天(上)
变量声明的时候建议 const优先,尽量使用const 原因: const语义化更好很多变量我们声明的时候就知道他不会被更改了,那为什么不用const呢?实际开发中也是,比如react框架,基本const如果你有纠结的时候&…...
【Leetcode】215. 数组中的第K个最大元素
一、题目 1、题目描述 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例1: 输入: [3,2,1,5,6,4], k = 2 输出…...

服务器数据恢复-RAID5常见故障的数据恢复方案
raid5阵列常见故障: 1、服务器硬件故障或者RAID阵列卡故障; 2、服务器意外断电导致的磁盘阵列故障; 3、服务器RAID阵列阵列磁盘出现物理故障,如:电路板坏、磁头损坏、盘面划伤、坏扇区、固件坏等; 4、误操作…...

12个VIM编辑器的高级玩法
vim 是一个很好用的编辑器,应用十分广泛。但关于 vim,总有一些你不知道的事情,我们需要持续不断的学习。 我经常使用 vim,也经常在各大社区、论坛看到 vim 专家用户分享经验,今天我们就总结其中常用的一部分ÿ…...
⽜客论坛的笔记
项目描述: 一个基本功能完整的论坛项目。项目主要功能有: 基于邮件激活的注册方式,基于MD5加密与加盐的密码存储方式,登录功能加入了随机验证码的验证,实现登陆状态检查、为游客与已登录用户展示不同界面与功能。支持用户上传头像,…...
JS逆向分析某枝网的HMAC加密、wasm模块加密
这是我2022年学做JS逆向成功的例子,URL:(脱敏处理)aHR0cHM6Ly93d3cuZ2R0di5jbi9hdWRpb0NoYW5uZWxEZXRhaWwvOTE 逆向分析: 1、每次XHR的GET请求携带的headers包括: {"X-ITOUCHTV-Ca-Timestamp":…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...

【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...