C++中vector的简单实现

文章目录

  • 一、主要任务
    • 1. 查看文档的网站的链接
    • 2.内部模拟的函数
  • 二、本人的模拟实现过程
    • 1. 所需模拟实现的函数
      • a.构造、拷贝构造
      • b. reverse()扩容
      • c.insert()、push_back()插入数据
      • d. erase()、pop_back()删除数据
      • e. swap()交换
      • f. begin()、end()非const与const迭代器
      • g. 完善构造函数之一:多个同数值参数构造
      • h. 完善构造函数之二:拷贝构造
      • i. 完善构造函数之三:迭代区间构造
      • j. 完善构造函数之四:初始化列表构造
      • k. 赋值重载
      • l. size()数据有效个数
      • m. resize()调整数据有效个数
      • n. operator[]()运算符重载
      • o. 析构函数
    • 2.内置类型声明
    • 3. 基础无参构造函数实现
    • 4. insert()插入
    • 5. capacity()计算容量
    • 6. reverse()扩容
    • 7. push_back()尾插
    • ※简单1-7 test()测试
    • 8. erase()删除
    • 9. pop_back()尾删
    • 10. swap()交换
    • 11. begin()、end()非const与const迭代器
    • ※测试iterator迭代器const与非const
    • 12.多个同数值构造
    • 13. 拷贝构造
    • 14. 迭代区间构造
    • 15. 初始化列表构造
    • 16. operator=()赋值重载
    • 17.size()有效数据个数
    • 18. resize()调整数据有效个数
    • 19.operator[]运算符重载
    • 20. 析构函数
    • ※[]与resize简单测试
  • 总结

一、主要任务

1. 查看文档的网站的链接

cplusplus网站
cppreference网站

2.内部模拟的函数


namespace myvector
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin();iterator end();const_iterator cbegin();const_iterator cend() const// construct and destroyvector()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()// capacitysize_t size() const ;size_t capacity() constvoid reserve(size_t n)void resize(size_t n, const T& value = T())///access///T& operator[](size_t pos)const T& operator[](size_t pos)const///modify/void push_back(const T& x)void pop_back()void swap(vector<T>& v);iterator insert(iterator pos, const T& x);iterator erase(Iterator pos)private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _endOfStorage; // 指向存储容量的尾};}
主要实现的函数有几个模块,分别为:
1、构造析构
2、容量大小与扩容
3、运算符重载
4、插入删除
5、迭代器
(这里直接使用迭代器来访问了,这里简单实现的迭代器就是原生指针)

二、本人的模拟实现过程

这里可以去看文档使用与模拟实现,有些缺省参数啥的我这就不实现了
有些缺省参数没学之前可以直接不用

1. 所需模拟实现的函数

用红色框框起来

a.构造、拷贝构造

在这里插入图片描述

b. reverse()扩容

在这里插入图片描述

reverse()扩容只需要传一个参数,,这个参数可能是最后的容量
在这里,扩容会有两种情况:
1、n > _endOfStorage就需要扩容(_endOfStorage是指向所开辟的总空间的最后一个的下一个位置)
(这里是指针,指向的确实是最后的下一个位置的空间如同capacity)
2、 n <= _endOfStorage就什么都不做,总容量不变,该函数不缩容

c.insert()、push_back()插入数据

在这里插入图片描述

(1)在position位置插入一个数据
(2)在position位置插入n个数据
(3)在position位置向后插入一段迭代区间
(4)不会玩
(5)插入一个initializer_list对象(常见的是这种:{value1, value2, ....})

在这里插入图片描述

这里push_back()重载成两个,我这只实现第一个
第一个和第二个的区别是后面的形参中是否带了const
但以我们习惯,插入的时候是不需要修改要插入的数据的
并且以外层来看,const对象也无法插入数据

d. erase()、pop_back()删除数据

在这里插入图片描述

erase()是用来删除数据的
第一个是删除position位置的数据
第二个是删除first到last的一段空间,删除范围为:[first, last)
我这简单实现第一个方法吧,主要都是挪动数据,第二个方法只要计算好从哪个挪动到哪个就好了

在这里插入图片描述

pop_back()删除最后一个数据
最后一个数据的位置就是类内置属性_finish的前一个位置

e. swap()交换

在这里插入图片描述

swap交换两个对象的内容
我们这里是以迭代器实现指向(本质也是指针)
这里的交换可以直接交换两个对象里面的属性的指向就好了
(如这里_start, _finish, _endOfStorage就是属性)

f. begin()、end()非const与const迭代器

在这里插入图片描述
在这里插入图片描述

begin()与end()是用来初始化迭代器或者给迭代器赋值的
有了begin()与end()方法,这里就可以使用迭代器变量了
另外,有了begin与end就可以使用范围for了,范围for底层是迭代器,不需要自己实现,编译器会自动转换
const与非const的区别就是无法修改内容而已,代码实现相似

g. 完善构造函数之一:多个同数值参数构造

在这里插入图片描述

这里是n个value构造一个对象

h. 完善构造函数之二:拷贝构造

在这里插入图片描述

拷贝构造是:使用已经存在的对象初始化另一个对象

i. 完善构造函数之三:迭代区间构造

在这里插入图片描述

迭代区间构造:使用其他对象的迭代区间来构造该对象
(这里挺有意思的,可以使用其他类对象的迭代区间构造)

j. 完善构造函数之四:初始化列表构造

在这里插入图片描述

初始化列表构造,初始化列表例如:{ 1,2,3,4 }这种的
使用也有两种方式:
1、vector<int> v1 = { 1,2,3,4 };
2、vector<int> v2({ 1,2,3,4 });
这样子也挺方便使用的

k. 赋值重载

在这里插入图片描述

赋值重载函数,上面图片的这里不重载,这里玩个新的
赋值重载如果使用const&则需要做扩容什么的,这里让编译器做

l. size()数据有效个数

在这里插入图片描述

很简单的函数,返回有效数据个数

m. resize()调整数据有效个数

在这里插入图片描述

实现第二个,能把空间初始化,第一个只是能扩大有效数据个数,但是里面的值可能是随机值
这里的n和string里的n是一样的功能:
(这里我把_finish - _start说成size吧,这也是有效数据个数)
(并且把_endOfStorage - _start 说成容量capacity)
1、如果n == size,则什么都不做
2、如果n < size,则把_finish调整到n位置(有效数据个数减小)
3、如果n > size && n <= capacity ,则把_finish调整到n位置,并且把旧_finish到新_finish的空间用value填充
4、如果n > capacity ,则扩容,并且把_finish调整到n位置,调整过来的空间也用value填充
(理论成立,实践开始)

n. operator运算符重载

在这里插入图片描述

重载[]是因为要下标访问,本来就是连续空间,还不能下标访问就扯蛋了
const对应const对象,非const对应普通对象,重载成两份

o. 析构函数

在这里插入图片描述

析构函数,对象生命周期结束自动调用
这里主要是释放空间,把申请的空间delete了

2.内置类型声明

在这里插入图片描述

这里的内置类型的typedef出来的指针
主要是:
1、类模版
2、模版类型指针typedef重命名为迭代器
3、迭代器管理数据的开始、结束和容量的末尾(本质也是管理指针)
(这里使用类模版主要是兼容各内置类型与自定义类型,编译器自动判别类型)

3. 基础无参构造函数实现

在这里插入图片描述

无参构造函数可以不实现,编译器会自动生成,但不同编译器可能不会初始化
这里使用了初始化列表,也可以在构造函数内赋值

4. insert()插入

在这里插入图片描述

这里我假设有revers()扩容函数和capacity()计算总容量的函数
这里主要流程思路是:
1、断言pos,不让pos是一个越界指针
2、判断_finish是否到尾了,容量是否不够了a. 不够容量,扩容,扩容机制是两倍,如果本来就为空就开始4个空间扩容之前得计算pos到_start的距离,以便扩容之后更新posb. 容量足够,继续下一步
3、定义一个指针来遍历移动数据,我这里是前一个数据移到当前位置,有兴趣可以做当前位置数据移到下一个位置
4、pos位置直接插入需要插入的数据
5、插入了数据_finish向后走一步
6、返回插入数据的位置的迭代器,主要是外面使用时需要更新迭代器

5. capacity()计算容量

在这里插入图片描述

这里capacity()就是为了计算总容量的,返回的是非负数(无符号整型)
咋计算剩余容量?在外部就是调用capacity() - size()不就可以了嘛
在内部就_endOfStorage - _finish,但通常是判断_endOfStorage == _finish
(这里使用const修饰是因为能方便const与非const对象使用)

6. reverse()扩容

在这里插入图片描述

reserve()主要做扩容,判断传参的n与总容量对比,n > capacity()就扩容
主要流程思维:
1、判断是否扩容(可以复用capacity()函数来判断)a. 不需要扩容,直接不运行if里面了b. 需要扩容,进入if执行语句
2、扩容前计算之前的有效数据个数(后面需要更新指针)
3、定义新变量接收申请空间
4、旧空间的数据循环赋值给新的空间
5、释放旧空间(内存泄漏问题很严重的啊)
6、_start指向新空间,_finish根据之前旧的数据个数指向新空间的有效数据个数末尾,_endOfStorage根据开的空间指向新空间
(这里如果不记录旧的空间有效数据个数,那么后面跟新_finish就会出问题,如果不更新,后面访问就找不到_finish)

我们这里清晰的看到注释了memcpy的内存字节拷贝函数,为啥呢?
因为内置类型可以,但vector也支持内置类型

例如:
vector<string> v; 这种样子的

在这里插入图片描述

memcpy()的拷贝就是让里面的指针指向了string那片空间
接下来的delete[],string对象会调用自己的析构,但tmp还指向那片string对象空间
等到最后vector对象也析构的时候对已经释放的空间进行第二次析构释放就会出问题所以,以上代码中是使用赋值,赋值是最简单的方法了
为啥赋值能过?
因为string类里有自己的赋值重载,一个存在的对象对另一个对象进行赋值不影响该对象的结构

7. push_back()尾插

在这里插入图片描述

push_back()可以直接复用insert()方法
insert()是在迭代器位置插入数据,而最后一个位置是_finish管理的
(也是指针,_finish指向最后一个数据的下一个位置)

※简单1-7 test()测试

在这里插入图片描述
在这里插入图片描述

因为insert()还没有迭代器的的遍历就没有测试
insert()可以简单的用push_back()简单测试
reverse()是扩容,在插入的时候自动扩容,搭配类构造函数看能不能正常扩容以上运行可以看出,从开始3个容量,插入的时候扩两倍
4个数据输入,3+4已经超过6个了,再次两倍扩容
根据这些可以看出扩容时正常的,数据最后位置插入正常

8. erase()删除

在这里插入图片描述

erase()删除pos位置数据,这里使用覆盖的方式
主要思想流程:
1、断言判断pos是否合法
2、定义另外一个迭代器,用于遍历覆盖
3、循环判断,这里使用后一个覆盖前一个
4、_finish减1,最后返回pos的位置以便于更新迭代器

9. pop_back()尾删

在这里插入图片描述

直接复用erase()方法
假设有一个方法end()是返回最后一个数据的下一个位置的迭代器
让这位置减1并让erase()执行删除
(当然也可以直接传_finish - 1,也可以直接 --_finish)

10. swap()交换

在这里插入图片描述

这里swap()交换主要是对象里属性的内容交换
一个指针的内容交换了,逻辑上指向也交换了
这里调用的是库中的swap()方法,简单交换可以这样子用挺好的

11. begin()、end()非const与const迭代器

在这里插入图片描述

主要思维不难
因为底层的属性也是迭代器iterator定义的,而iterator是用T*定义的,一层套一层
所以我知道,只需要返回指向数据的开始和指向数据的结束的迭代器就好了
这里哪怕没有实现什么++/--啥的也能用,底层不过是指针++/--而已
(注意,这里是使用原生指针实现,但stl库不是)

※测试iterator迭代器const与非const

在这里插入图片描述

这里范围for可以直接使用,因为类内部存在begin与end方法
(使用范围for类里面必须是begin与end名字,编译器只认这两个方法)
上面是两种迭代器的用法
(个人使用迭代器简单测试了,insert与erase方法是没问题的)

12.多个同数值构造

在这里插入图片描述

使用n个value构造对象
这里可以看到两个参数,一个是n,一个是const T& value
第一个容易理解,第二个不是那么容易理解
这里的T()是类型的默认构造形成的匿名对象,会有不理解内置类型(如整型)的怎么会有默认构造
这是C++为了方便通用,类是一个类型,类定义是一个对象,内置类型是类型,内置类型定义也成为了一个对象
如果是一个自定义类型,如vector<Date>,传进来前面的const T&就是类型(这里对象实例化的时候T已经出来了)
后面的T()调用了默认构造,也就是一个空对象,这时下面的赋值也不会出问题假如是vector<int>这种内置类型,是n个value构造对象并且不传第二个参数
如果没有自适应T()无法生成int对象(变量),那么value的值就不匹配,后面就没办法执行
所以这里的T()是能完成自定义类型与内置类型的默认构造的,内置类型也有默认构造了
主要思维流程:
1、复用reserve()方法,使用n给正在初始化的对象扩容
2、使用循环,复用push_back()方法直接尾插(因为不会改变value的值)
(因为在扩容的时候_endOfStorage已经指向后面,并且在push_back()的时候_finish已经走了,所以这都不用改变了)

13. 拷贝构造

在这里插入图片描述

主要思维流程:
1、使用人家的capacit()容量方法扩容
2、使用范围for遍历传进来的对象,并让每个内容push_back()在要实例化的对象
(因为push_bakc()不会改变传进来的实参)

14. 迭代区间构造

在这里插入图片描述

迭代区间如果不是定义赋值出问题,迭代区间不会出什么大问题
那里的函数模版,是因为不能确定是哪个类的迭代器,可以根据不同的类的迭代器生成不会的构造函数
这迭代区间初始化可以使用其他类对象的迭代区间,如这里是vector<int>的,可以给list<int>的迭代区间赋值

15. 初始化列表构造

在这里插入图片描述
在这里插入图片描述

初始列表这里比较特殊,可以查看手册
假设是vector<int> v1 = { 1,2,3,4,5 };
这里的括号就会经过隐式类型转换变成initializer_list
到里面的构造函数,因为initializer_list是一个类,形参是类对象
它也有它的迭代器,这个类挺方便的

16. operator=()赋值重载

在这里插入图片描述

看形参列表,因为是传值传参,编译器自动调用拷贝构造生成v
因为是走了拷贝构造,形参变化不改变实参,这里让形参和对象交换内容
那么,这里属性指向内容就发生了改变
返回的也是等号的右参数
出了作用域,v生命周期结束,自动调用它的析构

17.size()有效数据个数

在这里插入图片描述

有效个数就是_start到_finish之间的空间个数
因为在迭代器不飘的情况下_finish永远要大于等于_start
使用const修饰也是为了const对象与非const对象都能使用
(因为如果没有const,则const对象无法使用该方法,多写一个又没什么大作用,也没有修改)

18. resize()调整数据有效个数

在这里插入图片描述

我这里手搓的思维分为n == size、n > size、n < szie
1、n == size我什么都不做,直接返回
2、n > sizea. 我不知道会不会超出我的容量,但我reverse方法里面有判断,所以我直接交给reverse扩容b. 扩不扩容我都在_start到n位置做个标记位,以便我插入数据能走到那停(因为push_back()方法里面会自动走_finish,所以我不用自己手动走)
3、n < szie,直接把_finish调整到n的位置,也不用覆盖,_finish就是有效个数的限制
(这里以及上面说跳到n位置,连续的空间的指针可以当成数组来使用)

19.operator[]运算符重载

在这里插入图片描述

这里[]运算符重载,是为了下标访问
const修饰是为了对应const对象,另外一个就是普通对象的重载
这里思路很简单:
1、断言限制下标合法性
2、_start可以当成数组首元素地址,可以直接当数组使用,也可以*(_start + pos);

20. 析构函数

在这里插入图片描述

这里主要是释放申请的空间
因为_start指向申请的空间的第一个位置,所以直接delete[] _start就好了
最后让三个迭代器指向空,虽然对象不存在也无法再使用到他们了,但为了规范性

※[]与resize简单测试

在这里插入图片描述

可以看到[]运算符重载是能正常运行的,并且resize也能正常的扩容与缩小
默认值value填充也正常

总结

多用就会了,手搓理解就更深,因为这是一边写代码一边写博客的,可能展示上不是很好
但这里是我一边理解一边描述的
加油吧,少年
在这里插入图片描述

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

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

相关文章

mysql的存储结构

一个表就是一个ibd文件 .ibd文件大小取决于数据和索引&#xff0c;在5.7之后才会为每个表生成一个独立表空间即一个ibd文件&#xff0c;在此之前&#xff0c;所有表默认下都会存储在“系统表空间”&#xff08;共享表空间&#xff09;&#xff0c;所有表都在一个ibd文件。 inn…

示例六、湿敏传感器

通过以下几个示例来具体展开学习,了解湿敏传感器原理及特性&#xff0c;学习湿敏传感器的应用&#xff1a; 示例六、湿敏传感器 一、基本原理&#xff1a;随着人们生活水平的不断提高&#xff0c;湿度监控逐步提到议事日程上。由于北方地区秋冬季干燥&#xff0c;需要控制室内…

16.接口自动化学习-编码处理与装饰器

1.编码和解码 编码&#xff1a;将自然语言翻译成计算机可以识别的语言 hello–01010 解码&#xff1a;将机器识别的语言翻译成自然语言 2.编码格式 UTF-8 GBK unicode 3.编码操作 #编码操作str1"hello呀哈哈哈"str2str1.encode(gbk)print(str2)print(type(str2))…

js原型链与继承笔记

前置阅读&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain js中的“类”是一个函数。function test() {}中&#xff0c;test是由Function生成的。prototype与__proto__的区别&#xff1a; 前者是js函数&#xff08;C…

Linux学习之路 -- 文件系统 -- 缓冲区

前面介绍了文件描述符的相关知识&#xff0c;下面我们将介绍缓冲区的相关知识。 本质上来说&#xff0c;缓冲区就是一块内存区域&#xff0c;因为内核上的缓冲区较复杂&#xff0c;所以本文主要介绍C语言的缓冲区。 目录 1.为什么要有缓冲区 2.应用层缓冲区的默认刷新策略 …

如何在bud里弄3d模型?---模大狮模型网

随着数字化设计的不断发展&#xff0c;越来越多的设计软件提供了对3D模型的支持&#xff0c;为设计师们带来了更广阔的创作空间。Bud作为一款功能强大的设计工具&#xff0c;也提供了添加和编辑3D模型的功能&#xff0c;让用户能够更加灵活地进行设计创作。本文将为您详细介绍如…

【计算机网络】计算机网络体系结构

&#x1f6a9;本文已收录至专栏&#xff1a;计算机网络学习之旅 一.常见的三种结构 (1) OSI参考模型 为了使不同体系结构的计算机网络都能互连起来&#xff0c;国际标准化组织于1977年成立了专门机构研究该问题&#xff0c;提出了著名的开放系统互连基本参考模型&#xff0c…

pycharm 将项目连同库一起打包及虚拟环境的使用

目录 一、创建虚拟环境 1、用 anaconda 创建 2、Pycharm 直接创建 二、虚拟环境安装第三方库 1、创建项目后&#xff0c;启动终端(Alt F12)&#xff0c;或者点击下方标记处。 2、使用 pip 或者 conda 来进行三方库的安装或卸载 3、将项目中的库放入文档&#xff0c;便于…

李宏毅-注意力机制详解

原视频链接&#xff1a;attention 一. 基本问题分析 1. 模型的input 无论是预测视频观看人数还是图像处理&#xff0c;输入都可以看作是一个向量&#xff0c;输出是一个数值或类别。然而&#xff0c;若输入是一系列向量&#xff0c;长度可能会不同&#xff0c;例如把句子里的…

Spring STOMP-消息处理流程

一旦STOMP的接口被公布&#xff0c;Spring应用程序就成为连接客户端的STOMP代理。本节描述服务端消息处理的流程。 spring-messaging模块包含消息类应用的基础功能&#xff0c;这些功能起源于Spring Integration项目。并且&#xff0c;后来被提取整合到Spring框架&#xff0c;…

Spring Boot 调用外部接口的几种方式

Spring Boot 调用外部接口的几种方式 在微服务架构中&#xff0c;服务间的调用是不可或缺的环节。Spring Boot 为开发者提供了多种方式来实现这一任务&#xff0c;这个文章将为你详细介绍这些方式。 一、使用RestTemplate RestTemplate是 Spring Boot 早期版本中常用的 REST 客…

十个最适合论文写作的GPTs及其应用

文章目录 一、GPTs让一切皆有可能二、最适合论文写作的GPTs及其应用1、[Paper Search Engine](https://chat.openai.com/g/g-9v5gHG9Bo)2、[Academic Paper Specialist&#xff08;学术论文撰写专家&#xff09;](https://chat.openai.com/g/g-jryw3pfsH)3、[Paper Connect 论文…

微信公众号接入chatGPT自动回复(2)

微信公众平台 配置自动回复的服务器 application.properties中的配置 验证服务器接口配置 其实就两个接口(相同的url地址,只不过请求方式不一样) 1.验证接口(get请求) 2.自动回复接口(post请求) 完整代码 这个地址就是上面URL配置的地址 如果使用Nginx的话自动配置 将该代…

11个免费的 android数据恢复应用程序功能分析

在手机上丢失数据是一个很大的错误。但是&#xff0c;在这种情况下&#xff0c;除了惊慌失措之外&#xff0c;最好开始使用android数据恢复应用程序搜索以查找将其取回的方法。您可以检查手机的备份存储以在Android上进行数据恢复&#xff0c;但是如果数据仍然无处可寻&#xf…

@PostConstruct

PostConstruct initializeBean方法–> PostProcessor.postProcessMergedBeanDefinition --> InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction 被PostConstruct注解的方法会在Bean初始化的时候被调用&#xff0c;如下图&#xff1a; 继承关系如下…

jenkins连接ubuntu普通用户节点

1.创建credentials 2.创建node 3.在jenkins服务器还需要进行的操作&#xff08;jenkins服务器中&#xff09; mkdir /var/lib/jenkins/.ssh ssh-keyscan -H 192.168.110.204 >> /var/lib/jenkins/.ssh/known_hosts chown -R jenkins:jenkins /var/lib/jenkins/.ssh/ 4.…

相交链表(数据结构)

160. 相交链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/intersection-of-two-linked-lists/description/ 题目 解决思路 1&#xff0c;找到相交的点 相交链表的关键也就是找到相交的点&#xff0c;所以我们需要首先判断有没有相交的节点&#…

最新的云渲染100活动有哪些?渲染100邀请码1a12

随着科技的进步&#xff0c;云渲染已经成为设计行业的必备工具&#xff0c;各个云渲染平台为了吸引用户也推出各种各样的活动&#xff0c;今天我们以广受好评的渲染100为例&#xff0c;来说下它们的活动体系。 1、新用户活动 渲染100对新用户很友好&#xff0c;提供了充足的测…

K-RTD01和利时FW248中控卡件

K-RTD01和利时FW248中控卡件。 系统概述 的全称为保护工程师站及录波分析后台”是利用现代计算机和网络技术&#xff0c;K-RTD01和利时FW248中控卡件。实时收集变电站运行和故障信息&#xff0c;并通过对变电站的故障信息进行综合分析&#xff0c;K-RTD01和利时FW248中控卡件。…