c++ - vector容器常用接口模拟实现

文章目录

    • 一、成员变量
    • 二、常用迭代器接口模拟实现
    • 三、一些常用接口模拟
    • 四、默认成员函数
    • 五、功能测试


一、成员变量

我们通过在堆上申请一个数组空间来进行储存数据,我们的成员变量是三个指针变量,分别指向第一个位置、最后储存有效位置的下一个位置以及数组空间的最后位置的下一个位置。

//为了与stl保持一致使用重命名
typedef T* iterator;
typedef const T* const_iterator;iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;

在这里插入图片描述

二、常用迭代器接口模拟实现

直接返回数组的开始位置和有效数据的下一个位置即可。

//迭代器
//第一个位置
iterator begin()
{return  _start;
}//有效数据的下一个位置
iterator end()
{return _finish;
}//重载 const
const iterator begin() const
{return  _start;
}const iterator end() const
{return _finish;
}

三、一些常用接口模拟

(1)size
求有效数据的个数,用结束位置下一个位置的指针减去第一个位置的指针就是有效数据的个数了。

size_t size() const
{return _finish - _start;
}

(2)capacity
求容器的容量,用数组空间的最后位置的下一个位置的指针减去第一个位置的指针就是容量的大小了。

size_t capacity() const
{return _end_of_storage - _start;
}

(3)重载[]

//重载 []
T operator[](size_t i)
{//保证i位置符合assert(i < size());return  *(_start + i);
}const T operator[](size_t i) const
{//保证i位置符合assert(i < size());return  *(_start + i);
}

(4)reserve
预留空间,一般用于扩容,这里需要手动扩容,再拷贝数据,最后再改变成员变量的指向。

void reserve(size_t n)
{//先保存有效数据个数size_t _size = size();//判断是否需要扩容if (n > _size){//手动申请空间iterator tmp = new T[n];//拷贝数据到新空间for (size_t i = 0; i < _size; i++){*(tmp + i) = *(_start + i);}//释放掉原来的空间delete[] _start;//重新改变位置_start = tmp;_finish = _start + _size;_end_of_storage = _start + n;}
}

注意:
a.要先保存 size,因为在重新指向时需要_start第一个重新指向新空间,如果此时再去调用size()函数的话,返回的size是不确定的。最终导致_finish 指向错误。

b.就是不能使用memcpy函数进行拷贝,因为memcpy是浅拷贝,当出现数据是自定义类型而且也是需要申请资源的话就会出现重复释放等问题。如数据储存的是string类型。
在这里插入图片描述
所以需要使用深拷贝。
在这里插入图片描述
(5)clear
清空,将_start = _finish即可。

void clear()
{_finish = _start;
}

(6)insert
在迭代器位置前插入一个元素,并返回新的position位置。
迭起器失效问题:
插入一个位置可能会扩容,扩容就会导致position位置失效,所以如果需要扩容就先保存好 position 相对 _start的位置。

iterator insert(iterator position, const T val)
{//迭代器位置assert(position >= _start);assert(position <= _finish);//空间是否够if (_finish == _end_of_storage){	//保存相对位置size_t _size = position - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);//重新获取position位置position = _start + _size;}//往后移动一位iterator end = _finish;while (end > position){*end = *(end-1);--end;}*position = val;++_finish;return position;
}

(7)push_back
尾插入一个元素,这里直接调用insert即可。

void push_back(T x)
{insert(_finish, x);
}

(8)erase
删除position位置,返回position位置的值。
这里也可能会存在迭代器失效问题,如缩容(这里不考虑)、最后一个位置。

//删除迭代器
iterator erase(iterator position)
{//符合位置assert(position >= _start);assert(position < _finish);//覆盖掉positioniterator end = position;while (end < _finish - 1){*end = *(end + 1);++end;}--_finish;//这里返回空,因为position已经失效了if (position == _finish + 1){return nullptr;}return position;
}

(9)empty
是否空。

bool empty() const
{return size() == 0;
}

(10)pop_back
尾删,这里直接调用erase()即可。

void pop_back()
{if (empty())return;erase(_finish-1);
}

(11)resize
重新定义大小,n小于实际大小就缩小,n大于实际大小就用val来填充。

//重新定义大小void resize(size_t n, T val = T())
{if (n > capacity()){reserve(n);}size_t _size = size();if (n > _size){for (int i = _size; i < n; i++){//直接尾插push_back(val);}}else{_finish = _start + n;}}

(12)swap
交换两个容器。

void swap(vector<T>& x)
{//利用std库中的将指针位置交换即可std::swap(_start, x._start);std::swap(_finish, x._finish);std::swap(_end_of_storage, x._end_of_storage);
}

四、默认成员函数

(1)默认构造
无参构造,给了有默认值,这里不用初始化。

vector() {};

用n个val来初始化

vector(size_t n, T val = T())
{//先预留空间,这样就不用扩容了reserve(n);int i = 0;while (i < n){//尾插push_back(val);++i;}
};

拷贝构造
这里要用深拷贝

vector(vector<T> &x) 
{//先预留空间,这样就不用扩容了reserve(x.capacity());//直接将获取的数据插入即可,不用memcpy这种直接拷贝vector<T>::iterator it = x.begin();while (it != x.end()){push_back(*it);++it;}
};

用迭代器区间构造,因为可以用其他的容器来初始化,所以这里再用一个模板。

template <class InputIterator>
vector(InputIterator first, InputIterator last)
{//先预留空间,这样就不用扩容了reserve(last - first);while (first < last){push_back(*first);++first;}
}

因为迭代器区间的构造函数与用n个val来初始化构造会冲突。
在这里插入图片描述

为了解决这个问题,使用一个具体一点的n个val来初始化的构造函数

vector(int n, T val = T())
{reserve(n);int i = 0;while (i < n){push_back(val);++i;}
};

(2)析构函数

~vector()
{//释放delete[]_start;_start = _finish = _end_of_storage = nullptr;
}

(3)重载 =

vector<T>& operator=(vector<T> x)
{//利用交换函数,x出了该函数作用域就会销毁,所以不用我们手动销毁原来空间了swap(x);return *this;
}

五、功能测试

(1)内置类型

void test01()
{//n个valxu::vector<int> v1(2,1);for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;//区间xu::vector<int> v2(v1.begin(), v1.end());for (int i = 0; i < v1.size(); i++){cout << v2[i] << " ";}cout << endl;//赋值xu::vector<int> v3;v3 = v1;for (int i = 0; i < v1.size(); i++){cout << v3[i] << " ";}cout << endl;v1.push_back(2);v1.insert(v1.begin(), 0);for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.pop_back();v1.erase(v1.begin());for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.resize(10, 5);for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;}

在这里插入图片描述

(2)自定义类型

void test02()
{//n个valxu::vector<string> v1(2, "aa");for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;//区间xu::vector<string> v2(v1.begin(), v1.end());for (int i = 0; i < v1.size(); i++){cout << v2[i] << " ";}cout << endl;//赋值xu::vector<string> v3;v3 = v1;for (int i = 0; i < v1.size(); i++){cout << v3[i] << " ";}cout << endl;v1.push_back("bb");v1.insert(v1.begin(), "cc");for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.pop_back();v1.erase(v1.begin());for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;v1.resize(10, "ee");for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/16838.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

特殊矩阵的压缩矩阵

目录 前提条件&#xff1a; 类型&#xff1a;对称矩阵&#xff0c;三角矩阵、三对角矩阵、稀疏矩阵 1&#xff1a;对称矩阵&#xff1a; 定义&#xff1a;n阶矩阵A 中任意一元素都有ai,jaj,i(1<i,j<n) 图像&#xff1a; 表达式&#xff1a; 计算过程&#xff1a; …

stream-并行流

定义 常规的流都是串行的流并行流就是并发的处理数据&#xff0c;一般要求被处理的数据互相不影响优点&#xff1a;数据多的时候速度更快&#xff0c;缺点&#xff1a;浪费系统资源&#xff0c;数据少的时候开启线程更耗费时间 模版 Stream<Integer> stream1 Stream.of…

【YOLO 系列】基于YOLO V8的学生上课行为检测系统【python源码+Pyqt5界面+数据集+训练代码】

前言 在现代教育环境中&#xff0c;学生上课行为的监测对于提升教学质量和学生学习效率具有重要意义。然而&#xff0c;传统的人工观察方法不仅效率低下&#xff0c;而且难以保证客观性和准确性。为了解决这一问题&#xff0c;我们启动了这个项目&#xff0c;目的是利用YOLOV8…

AI数学知识

AI数学知识 1、线性代数相关&#xff08;矩阵&#xff09;1、什么是秩2、奇异值分解3、特征值分解和奇异值分解4、低秩分解 回归分类知识点2、概率论相关1、先验概率和后验概率2、条件概率、全概率公式、贝叶斯公式、联合概率3、最大似然估计4、贝叶斯公式和最大似然估计5、伯努…

深入理解Kubernetes的调度核心思想

一、引言 Kubernetes&#xff08;简称K8s&#xff09;是一个开源的容器编排系统&#xff0c;用于自动化部署、扩展和管理容器化应用程序。在Kubernetes集群中&#xff0c;调度器是一个核心组件&#xff0c;它负责将Pod&#xff08;Kubernetes中的最小部署单元&#xff09;分配…

Java学习16

目录 一.StringBuffer类&#xff1a; 1.基本介绍&#xff1a; 2.StringBuffer的构造器&#xff1a; 3.String与StringBuffer的相互转换&#xff1a; &#xff08;1&#xff09;String->StringBuffer &#xff08;2&#xff09;StringBuffer->String 4.StringBuffer…

Redis篇 数据的编码方式和单线程模型

编码方式和单线程模型 一.redis中的数据类型二. Redis中查询编码方式命令三. 单线程模型四. 经典面试题,redis为何这么快?什么是IO多路复用? 一.redis中的数据类型 在redis中,数据类型大致分为5种 1.字符串类型 2.哈希 3.列表 4.集合 5.有序集合 redis底层在实现这些数据结构…

防火墙技术基础篇:NAT转发之——NAPT(同时转换地址和端口)

NAT转发之——NAPT&#xff08;同时转换地址和端口&#xff09; 网络地址端口转换NAPT 网络地址端口转换NAPT&#xff08;Network Address Port Translation&#xff09;是人们比较熟悉的一种转换方式。NAPT普遍应用于接入设备中&#xff0c;它可以将中小型的网络隐藏在一个合…

Vue2和Vue3生命周期的对比

Vue2和Vue3生命周期的对比 Vue2 和 Vue3 生命周期对照表Vue2 和 Vue3 生命周期图示 Vue2 和 Vue3 生命周期对照表 触发时机Vue2.xVue3.x组件创建时运行beforeCreate setup createdsetup 挂载在DOM时运行beforeMountonBeforeMountmountedonMounted响应数据修改时运行beforeUpdat…

x264 码率控制原理:x264_ratecontrol_end 函数

x264_ratecontrol_end 函数 原理 函数功能:编码完一帧数据后,保存状态并更新 ratecontrol 状态。函数参数:x264_t *h:编码器上下文结构体int bits:编码该帧所用的比特数int *filler:用于返回一个填充比特数函数调用关系: 函数内部执行流程:初始化x264_ratecontrol_t结…

关于DDos防御...别在听别人瞎扯了.....

前言 无意间刷文章的时候看到一篇文章&#xff0c;写的是遇到ddos&#xff0c;怎么用iptables封IP....... 然后我就百度搜了一下&#xff0c;好多都是这么说的&#xff0c;但是我发现&#xff0c;大多数人只要遭受过长期Ddos的&#xff0c;就不会再信网上的文章 文笔不太好&…

在64位程序中调用SetWindowLong指定窗口处理过程失效问题排查(附C++编译器数据模型)

C软件异常排查从入门到精通系列教程&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#xff09;https://blog.csdn.net/chenlycly/article/details/125529931C/C基础与进阶&#xff08;专栏文章&#xff0c;持续更新中...&#xff09;https://blog.csdn…

手把手实现AVL——二叉平衡搜索树

概述&#xff1a;本文介绍AVL树的实现&#xff0c;从零构建一颗AVL树&#xff0c;以及对应的插入、删除、旋转操作 什么是AVL树&#xff1f; AVL树是带有平衡条件的二叉查找树&#xff0c;二叉查找树又区别于二叉树&#xff1a;保证有序 这个平衡条件是每个节点的左右子树高…

[数据集][目标检测]红外人狗检测数据集VOC+YOLO格式185张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;185 标注数量(xml文件个数)&#xff1a;185 标注数量(txt文件个数)&#xff1a;185 标注类别…

Python数据分析常用函数

Python基础 数字处理函数 Python提供了用于数字处理的内置函数和内置模块(math)&#xff0c;使用内置模块&#xff0c;需要先导入 import math。 内置函数math模块abs(-5)返回绝对值math.ceil(2.3)返回不小于x的最小整数divmod(9,4)返回商和余数math.floor(2.3)返回不大于x的…

GLCM 特征和LBP特征提取

GLCM 特征 GLCM&#xff08;灰度共生矩阵&#xff09;特征用于描述图像中像素灰度级之间的空间关系&#xff0c;常用于纹理分析。GLCM特征通过统计图像中各个灰度级对之间的出现频率来描述图像的纹理特征。GLCM特征包括能量&#xff08;ASM&#xff09;、对比度&#xff08;Co…

对于创建相关项目时,项目出现红色感叹号,且无jre环境显示,应该怎么解决?

首先&#xff0c;假设你已经下载好了相关你的jre环境&#xff0c;注意&#xff1a;如果你的jre不想用之前用的默认的话&#xff0c;你应该新建一个新的文件路径来存储你的新的jre环境下的项目文件。 先直接new->project->javaproject 点击next: 显示如下&#xff1a;&…

JavaScript表达式语句二

异常处理语句 异常标识一种非中正常得信息&#xff0c;它提示程序在运行过程中发生了意外或错误&#xff0c;然后JavaScript通过一定的方式把它暴露出来&#xff0c;这叫做抛出异常。抛出异常操作表示系统告诉我们当前程序出现了问题&#xff0c;JavaScript使用异常处理语句来…

以一道简单的例题计算灵敏性分析

在例1.1中&#xff0c;全部的变量包括&#xff1a;猪的重量w(磅),从现在到出售猪期间经历的时间t(天),t天内饲养猪的花费C(美元),猪的市场价格p(美元/磅),售出生猪所获得的收益R(美元),我们最终获得的净收益P(美元).这里还有一些其他的有关量&#xff0c;如猪的初始重量(200磅)…

Python 全栈体系【四阶】(五十四)

第五章 深度学习 十二、光学字符识别&#xff08;OCR&#xff09; 3. 文字识别技术 3.1 CRNNCTC(2015) CRNN&#xff08;Convolutional Recurrent Neural Network&#xff09;即卷积递归神经网络&#xff0c;是DCNN和RNN的组合&#xff0c;专门用于识别图像中的序列式对象。…