当前位置: 首页 > news >正文

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参数校验函数&#xff0c;如果不合法抛出自定义异常&#xff01; 3 配置全局异常 1 自定义参数校验异常 # 1.用户自定义异常类型&#xff0c;只要该类继承了Exception类即可 class ValDtoError(Exception):# 初始化def __in…...

Python综合练习题

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

SpringCloud+Nacos集成Seata-1.7.0分布式事务

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

任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK

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

修炼k8s+flink+hdfs+dlink(六:学习k8s)

一&#xff1a;增&#xff08;创建&#xff09;。 直接进行创建。 kubectl run nginx --imagenginx使用yaml清单方式进行创建。 二&#xff1a;删除。 kubectl delete pods/nginx 三&#xff1a;修改。 kubectl exec -it my-nginx – /bin/bash 四&#xff1a;查看。 …...

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-[4]客户端与服务端连接

红队专题 招募六边形战士队员服务端编写新建工程server函数创建主线程类获取配置信息运行command 命令头文件里创建引用win32 类库/头文件startsocket 开始监听 类函数添加类StartSocketmysend/myrecv 设置 m_sockCommon 头文件MSGINFO_S 结构体 ThreadMain头文件runflag 启动 …...

Qt Designer生成ui文件,如何转py文件,如何运行

下面将逐步介绍ui文件如何转py文件&#xff0c;怎么运行的具体操作步骤 ui文件转py文件 1.使用Qt Designer生成ui文件&#xff0c;保存到本地 2.输入 cmd &#xff0c;打开命令行窗口 3.进入ui文件的目录下&#xff0c;文件路径使用你本地存放ui文件的位置 cd /d ui文件路径…...

Python数据挖掘:自动售货机销售数据分析与应用

&#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于恒川的日常汇报系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏C语言初阶、C…...

【设计模式】设计模式概述

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…...

第六届“中国法研杯”司法人工智能挑战赛进行中!

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

关于 passing ‘const xx’ as ‘this’ argument of 的错误

今天在写一个简单的函数时&#xff0c;编译时出现了如下的错误&#xff1a; 这个很简单的函数是这样的&#xff1a; struct bundle_set {uint32_t baseId;uint32_t endId;bool operator< (const bundle_set &a){return baseId < a.baseId;} }; 在网上搜索到都是说什…...

数据结构和算法(13):优先级队列

概述 按照事先约定的优先级&#xff0c;可以始终高效查找并访问优先级最高数据项的数据结构&#xff0c;也统称作优先级队列 优先级队列将操作对象限定于当前的全局极值者。 根据数据对象之间相对优先级对其进行访问的方式&#xff0c;与此前的访问方式有着本质区别&#xf…...

面试经典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优先&#xff0c;尽量使用const 原因&#xff1a; const语义化更好很多变量我们声明的时候就知道他不会被更改了&#xff0c;那为什么不用const呢&#xff1f;实际开发中也是&#xff0c;比如react框架&#xff0c;基本const如果你有纠结的时候&…...

【Leetcode】215. 数组中的第K个最大元素

一、题目 1、题目描述 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例1: 输入: [3,2,1,5,6,4], k = 2 输出…...

服务器数据恢复-RAID5常见故障的数据恢复方案

raid5阵列常见故障&#xff1a; 1、服务器硬件故障或者RAID阵列卡故障&#xff1b; 2、服务器意外断电导致的磁盘阵列故障&#xff1b; 3、服务器RAID阵列阵列磁盘出现物理故障&#xff0c;如&#xff1a;电路板坏、磁头损坏、盘面划伤、坏扇区、固件坏等&#xff1b; 4、误操作…...

12个VIM编辑器的高级玩法

vim 是一个很好用的编辑器&#xff0c;应用十分广泛。但关于 vim&#xff0c;总有一些你不知道的事情&#xff0c;我们需要持续不断的学习。 我经常使用 vim&#xff0c;也经常在各大社区、论坛看到 vim 专家用户分享经验&#xff0c;今天我们就总结其中常用的一部分&#xff…...

⽜客论坛的笔记

项目描述: 一个基本功能完整的论坛项目。项目主要功能有: 基于邮件激活的注册方式&#xff0c;基于MD5加密与加盐的密码存储方式&#xff0c;登录功能加入了随机验证码的验证&#xff0c;实现登陆状态检查、为游客与已登录用户展示不同界面与功能。支持用户上传头像&#xff0c…...

JS逆向分析某枝网的HMAC加密、wasm模块加密

这是我2022年学做JS逆向成功的例子&#xff0c;URL&#xff1a;&#xff08;脱敏处理&#xff09;aHR0cHM6Ly93d3cuZ2R0di5jbi9hdWRpb0NoYW5uZWxEZXRhaWwvOTE 逆向分析&#xff1a; 1、每次XHR的GET请求携带的headers包括&#xff1a; {"X-ITOUCHTV-Ca-Timestamp":…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...