c++还原简单的vector

news/2024/4/29 12:15:27/文章来源:https://blog.csdn.net/m0_71841506/article/details/128204911

image-20221206160819924

文章目录

  • vector
      • vecotor的介绍
    • vector的模拟实现
      • 类的框架
      • 成员变量
      • 迭代器
      • 构造函数
      • 析构函数
      • size()
      • capacity()
      • operator[]重载
      • 扩容
      • resize()
      • 尾插
      • 验证是否为空
      • 尾删
      • clear 清除
      • swap交换
      • insert插入
      • erase删除
      • 迭代器区间初始化构造函数
      • 拷贝构造
      • 赋值运算符重载
      • n个val构造函数
      • 再谈构造函数

vector

vecotor的介绍

1.vector是表示可变大小数组的序列容器

2.就像数组一样,vector也采用连续存储空间来存储元素,意味着可以采用下标对vector的元素进行访问。(和数组一样高效)

3.vector使用动态分配数组来存储元素,当新元素插入时,而旧的空间不够时,一般分配一个新的数组,然后把全部元素移到新的数组,再进行新元素的插入。

4.vector会分配一些额外的空间来适应可能的增长,因此存储空间比实际需要的存储空间更大。

库里vector

vector的模拟实现

类的框架

namespace Vect
{template<typename T>//模板参数class vector{
public:成员函数......
private:成员变量......};
}

成员变量

	private:iterator _start;//从0开始iterator _finish;//最后一个成员变量的下一个位置iterator _endofstorage;//容量

迭代器

这里的迭代器iterator可看作为指针

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()//构造函数//这里用初始化列表比较方便:_start(nullptr),_finish(nullptr),_endofstorage(nullptr){}

析构函数

~vector()//析构函数{delete[] _start;_start = _finish = _endofstorage = nullptr;}

size()

指针相减 得指针之间成员个数

size_t size() const//size---有效成员个数{return 	_finish - _start;}

capacity()

	size_t capacity()const//capacity---容量{return _endofstorage - _start;}

operator[]重载

T& operator[](size_t pos){assert(pos < size());return T[pos];}const T& operator[](size_t pos) const{assert(pos < size());return T[pos];}

扩容

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();//记录成员个数T* tmp = new T[n];//开辟新空间if (_start)//如果_start指向的空间不为空就要拷贝数据{//把_start的数据拷贝到tmp上面//memcpy(tmp, _start, sizeof(T) * oldsize);//这里实现了浅拷贝//后面会谈到memcpy的弊端for(size_t i=0;i<oldsize;i++){tmp[i]=_start[i];//这里实现深拷贝}//删除旧空间-空间不为空才需要释放delete[]_start;}_start = tmp;//指向新空间
//原来的size=_finish-_start,而此时的_start是新空间的_start,   //_finish是旧空间的_finish,相减得出不是之间成员个数了;所以我们要用oldsize()来记录先前的成员个数,用新的_start+oldsize()得出新的_finish_finish = _start + oldsize;_endofstorage = _start + n;}
}

resize()

image-20221206160315297

void resize(size_t n, T val = T())//value_type val 给匿名对象
//n<size():缩容;
//size()<n<capacity():用val初始化有效成员以外的成员变量;
//n>capacity():扩容且用val初始化有效成员以外的成员变量;		
{if (n > capacity())//n大于容量{reserve(n);//扩容}if (n > size()){while (_finish < _start + n){//用val初始化有效成员以外的成员变量;*_finish = val;++_finish;}}else{_finish = _start + n;//缩size()}}

尾插

image-20221206160248020

void push_back(const T& x)//尾插{if (_finish == _endofstorage)//扩容{size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}

验证是否为空

image-20221206160336743

bool empty()const{return _start == _finish;}

尾删

image-20221206160354972

void pop_back(){assert(!empty());--_finish;}

clear 清除

image-20221206160408484

void clear()//清理---不影响容量{_finish = _start;}

swap交换

image-20221206160423909

void swap(vector<T>& v)//交换
{//这里要指明是std库里的swap,因为头文件展开后从上往下找swap函数不一定先找到的是std库里的swap,还可能是vector的swapstd::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage,v._endofstorage);}

insert插入

这里我们实现第一个接口

image-20221206160542301

//迭代器失效:当插入时要扩容,pos指针指向原来的空间,而_start指向新空间,在挪动数据时会出问题iterator insert(iterator pos, const T& val){//pos要在_start和_finish之间assert(pos >= _start);assert(pos < _finish);//_start为空时插入要扩容或者容量满了都要扩容if (  _finish==_endofstorage){size_t len = pos - _start;//记录pos的相对位置size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;//针对迭代器失效要对pos更新}iterator end = _finish - 1;while (end >= pos)//挪动数据{*(end + 1) = *(end);--end;}pos = val;++_finish;	return pos;}

erase删除

这里我们实现第一个接口

image-20221206160618001

	iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator begin=pos+1;while (begin<_finish){*(begin-1) = *(begin );++begin;}--_finish;return pos;//返回迭代器}

在这里我们创建一个vector往里面尾插1234;之后我们删除4;并且打印删除后的迭代器位置

image-20221205160153100

这里我们运行了看起来没有问题,实际上问题很大!删除4后容器的数据只有123,而我们访问到了4—野指针越界访问! 这里我们实现的模拟跟Linux系统下相似,所以在Linux系统下也不会报错;但我们如果删除的是2或者是3呢?会越界吗?换句话说:迭代器会失效吗?答:在Linux系统下迭代器可能会失效也可能不会!但在vs下必然失效!

这里我们换库里面的vector的erase试一下

image-20221205161257821

果然在vs下对迭代器做了更严格的检查,读都不给读,更何况是写;—迭代器失效了吗???好像失效了,这里有人会说你这个删除4肯定是越界了,那我删除别的对象不就不越界了嘛?

好现在我们删除2

image-20221205161333702

照样报错!所以我们使用完迭代器之后最好就不要再用迭代器了

如果硬要用就更新迭代器!

删除4—会被断言出越界

image-20221205162933187

删除2—更新了迭代器不会报错

image-20221205163424455

现在在vs还是Linux下都不会发生迭代器失效了,若越界直接断言!

迭代器区间初始化构造函数

		template <class InputIterator>vector(InputIterator first, InputIterator last)//构造函数:用迭代器区间去初始化: _start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);++first;}}

拷贝构造

	vector(const vector<T>& v)//拷贝构造:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)//因为要扩容,所以要提前初始化三个迭代器
{
vector<T> tmp(v.begin(),v.end());//用迭代器构造tmp;因为是用的const+引用所以要有中间者tmpswap(tmp);//this和临时对象tmp交换}

赋值运算符重载

vector<T>& operator=( vector<T> v)//传值{
//赋值的时候如果容量满了会扩容,就算是自己赋值給自己也会扩容;先后经过后者拷贝构造临时对象,拷贝构造时用到迭代器区间构造临时对象,//然后构造时就用到尾插push_back,尾插时就验证要不要扩容!swap(v);//this和v交换return *this;}

n个val构造函数

		vector(size_t n, const T& val = T())//库里面給的是size_t n: _start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);//扩容for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T())//int模式构造函数的n重载size_t模式构造函数的n: _start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);//扩容for (int i = 0; i < n; i++){push_back(val);}}

上面出现了size_t n的构造函数和int n的构造函数

我们做个实验,把int n的构造函数注释掉;然后用10个5初始化构造v

image-20221205211232270

运行后发现报错了,原因是非法的间接寻址。这是为啥呢?

我们看到上面10个5初始化,类型为**(int ,int)** ;而n个val的参数类型为**(size_t ,int)**

另一个迭代器区间初始化的参数类型为**(InputIterator first, InputIterator last)**

分别为两个参数模板类型,而int参数也可以作为参数模板

(int,int)参数类型进到**(size_t,int)参数类型前者int还需要整形提升**;而对于(InputIterator first, InputIterator last)类型(int,int)可以直接进入。但到后面的地址解引用就是非法寻址了!

image-20221205220032278

所以我们还需要在写一个(int,int)参数类型的构造函数重载(size_t ,int)构造函数

再谈构造函数

这里我们创建一个4个vector<vector<int>>,給vector<int>序列初始化为11

然后我们打印出来

image-20221205224024586

我们打印四个vector<int>,没毛病。我们打印五个:报错了!

image-20221205224221463

这是为啥呢?我们前面写到构造函数扩容,size达到4时要扩容,前者四个没报错而后者5个报错了!这说明扩容有问题!

image-20221205225959910

构造函数用到push_back,数量达到4个要扩容,而这里的扩容是先new一块8个对象(类型为vector<int>)大的新空间tmp(类型为vector<vector<int>>),把旧空间的vector<int>的_start一个个拷贝(这里是memcpy-浅拷贝)到新空间;再delete旧空间vv(先依次析构v再析构空间vv);再把 vv的 _start指向新空间tmp;这时我们发现vv扩容为tmp是完成了深拷贝(把v的 _start一个个拷贝到tmp上),而v却还是浅拷贝( v的 _start 还是指向原来的空间,而原来的空间已经被析构了—属于野指针越界访问!)这里配合着下图理解

image-20221205231309672

所以我们要换种方式扩容拷贝

		void reserve(size_t n){if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];//开辟新空间if (_start)//如果_start指向的空间不为空就要拷贝数据{for (size_t i = 0; i < oldsize; ++i){tmp[i] = _start[i];//复用运算符重载=完成深拷贝delete[]_start;//删除旧空间-空间不为空才需要释放}}_start = tmp;//指向新空间_finish = _start + oldsize;_endofstorage = _start + n;}}

image-20221205232650522

好啦,vector的模拟实现就到这了。小伙伴们要多多实现加深印象噢!

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

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

相关文章

m基于遗传优化的复杂工序调度matlab仿真,输出甘特图和优化收敛图

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 遗传算法 (Genetic Algorithm&#xff0c;GA) 是一种基于规律进化的随机优化搜索算法&#xff0c;该算法最早是由Holland在1975年提出的。遗传算法的主要优势是通过对目标对象进行优化操作&#…

三面:请设计一个虚拟DOM算法吧

一、问题剖析 这不是前几天面试官开局面试官就让我设计一个路由&#xff0c;二面过了&#xff0c;结果今天来个三面。 问你道简单的送分题&#xff1a;设计一个虚拟DOM算法&#xff1f; 好家伙&#xff0c;来吧&#xff0c;先进行问题剖析&#xff0c;谁让我们是卑微的打工人…

数据存储,详细讲解

✨数据存储&#xff0c;详细讲解&#x1f49c;数据类型的介绍&#xff1a;&#x1f499;整形的内存存储大小端介绍&#xff1a;&#x1f49b;浮点数的存储&#x1f49c;数据类型的介绍&#xff1a; 1.内置类型&#xff1a; char //字符数据类型&#xff08;1&#xff…

POI在指定excel插入行java

我想在第三行&#xff0c;插入数据库的数据&#xff0c;这里假如数据库有10条&#xff0c;并且继承第二行的格式 数据库数据 {"clark",25}&#xff0c;我写个json对象&#xff0c;10条这个 造数据代码 JSONArray jsonArray new JSONArray();for (int i 0; i <…

Azkaban登录分析

分析意义:目前azkaban采用的是azkaban-users.xml配置文件的方式,配置登录用户。如果公司需要二次开发,增加安全性和便捷性,想从数据库取值呢,该如何着手开发呢?本文分析登录过程,便于进行azkaban的二次登录开发。 1、登录请求地址,请求方式和参数 请求地址:http://x…

计讯物联数字乡村解决方案全力助推三农信息化建设

​2020年&#xff0c;中央网信办等七部门联合印发《关于开展国家数字乡村试点工作的通知》。《通知》提出&#xff0c;做好数字乡村发展整体规划设计&#xff0c;统筹推进乡村的新型基础设施、数字经济、数字农业农村、农村科技创新、乡村数字治理、信息惠民服务等建设和发展。…

TF卡格式化了怎么办?tf卡数据恢复,看这3个方法

现在手机存储卡都很普及&#xff0c;TF卡是最常见的存储卡之一。但是你知道吗&#xff1f;TF卡也会有问题&#xff0c;比如出现误删数据&#xff0c;或者把数据格式化。因为手机内存有限&#xff0c;我们经常会把 TF卡设置为默认的最大空间&#xff0c;这样就可能会出现存储空间…

中国书画院院士、著名画家——戴友

戴友 戴友 中国书画院院士、著名画家 广州美术学院国画系毕业的专业画家 师从著名国画大家关山月、黎雄才、方楚雄、周波 艺术简介 戴友&#xff0c;著名画家、中国书画院院士。1960年生于广东&#xff0c;江苏省溧阳市人&#xff0c;汉族。自幼自学绘画&#xff0c;1991年…

DolphinDB 诚挚招募实施伙伴

随着 DolphinDB 业务发展&#xff0c;为满足迅速增长的客户需求&#xff0c;我们现正式启动“实施伙伴招募计划”。DolphinDB 客户已经涵盖7家Top 10券商、头部公募及私募基金、知名银行、交易所、世界500强制造业客户、标杆能源企业等&#xff0c;我们非常期待和欢迎实施伙伴们…

【C语言程序设计】实验 3

目录 1. 水仙花数 2. 五位回文数 3. 输入x&#xff0c;计算y 4. 百分制改为等级制 5. 同构数 6. 月份天数 7. 加一天后日期&#xff08;条件&#xff09; 8. 计算服装款&#xff08;条件&#xff09; 1. 水仙花数 【问题描述】输入一个3位正整数&#xff0c;判断该…

精彩数据:2021年我国民旅客周转量6530亿公里,审定受理飞机2803架

2021年是特殊的一年&#xff0c;全体民航成员在努力克服疫情防控、经营亏损、安全压力等困难交织叠加的影响下&#xff0c;切实的推动了民航的高质量发展&#xff0c;再各项工作上都取得了较好的成绩。下面是小编使用可视化互动平台对民航发展统计报告进行报表数据处理分析后得…

Word控件Spire.Doc 【图像形状】教程(12) 如何在C#中旋转word文档上的形状

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

C#学习记录——.NET Framework的组成及C#程序的执行过程

『好好学习&#xff0c;天天向上。』—— 毛主席语录 .NET Framework的组成 .NET Framework 是由微软公司推出的一种完全面向对象的软件开发平台&#xff0c;它主要由两个组件构成&#xff0c;分别为公共语言运行库&#xff08;CLR&#xff09;和.NET Framework类库&#xff…

深度学习-第P1周——实现mnist手写数字识别

深度学习-第P1周——实现mnist手写数字识深度学习-第P1周——实现mnist手写数字识一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据集3、数据可视化四、构建简单的CNN网络五、训练模型1、设置超参数2、编写训练函数3、编写测试函数4、正式训练六、结果可视化…

机器学习与深度学习的基本概念

目录 机器学习是什么&#xff1f; 机器学习的任务 回归Regression 分类Classification 创造学习Structed Learing 机器学习怎么找这个函数 定义含未知参数的函数 定义loss损失函数 定义优化器optimization 写出一个更复杂的有未知参数的函数 sigmoid 基本推理过程 si…

一套Altair Feko复杂结构模型散射和天线辐射仿真建模攻略

导读&#xff1a;Feko软件广泛应用于电磁散射、电磁辐射仿真&#xff0c;例如&#xff1a;天线、天线布局、天线罩、屏蔽效能、电磁散射、频选结构、线束EMC等方面。问题种类繁多&#xff0c;但是无论仿真哪一类问题&#xff0c;其仿真流程是相同的&#xff0c;我们只需掌握了这…

使用 Echarts 插件完成中国旅游地图

目录前言&#xff1a;什么是 Echarts 插件具体实现思路中国旅游地图成品展示步骤&#xff1a;完成中国旅游地图代码总结&#xff1a;前言&#xff1a; 大家都知道&#xff0c;一般情况下&#xff0c;想要使用前端设置一个 中国旅游地图 需要使用 canvas 画布进行编写&#xff…

基于ARM架构openEuler系统通过qemu模拟器自动安装启动ARM架构的openEuler虚拟机

【原文链接】基于ARM架构openEuler系统通过qemu模拟器自动安装启动ARM架构的openEuler虚拟机 文章目录一、基础准备工作二、自动创建基于dhcp自动获取ip地址的openEuler虚拟机三、自动创建配置静态IP地址的openEuler虚拟一、基础准备工作 &#xff08;1&#xff09;下载ARM架构…

[附源码]Python计算机毕业设计Django校园订餐系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Linux|树莓派】分文件编程以及静态库动态库

一、分文件编程 简单来说树莓派的分文件编程就是将一个项目的代码放在不同的文件里面&#xff0c;然后在主函数添加一个头文件&#xff0c;这样会使#控制字体颜色主程序变得简单。 在编译的时候要将主函数和功能函数一起编译&#xff1a; 注意&#xff1a;include <stdio.h…