C++:vector的模拟实现

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

文章目录

目录

文章目录

前言

一、vector的模拟实现

1.1 迭代器的获取

1.2 构造函数和赋值重载 

1.2.1 无参构造函数

1.2.2  有参构造函数(对n个对象的去调用他们的构造)

1.2.3 迭代器区间构造 

1.2.4 赋值重载 

 1.3 析构函数

 1.4 常见接口

1.4.1 获取size和capacity

1.4.2 reserve提前扩容 

1.4.3 resize 

1.4.4 insert

1.4.5 erase

1.4.6 push_back()

1.4.7 pop_back()

1.4.8 swap 

 1.4.9 重载[ ]

 1.5. 迭代器失效问题

1.5.1 insert的失效 

1.5.2 erase的失效 

 二 vector实现的全部代码

总结


前言

本篇详细介绍了vector的模拟实现,让使用者了解vector,而不是仅仅停留在表面,更好的模拟,为了更好的使用. 文章可能出现错误,如有请在评论区指正,让我们一起交流,共同进步!


一、vector的模拟实现

 大致框架需要有模板(类外定义)/迭代器以及迭代器的获取(public定义,要有可读可写的也要有可读不可写的)/成员变量(private定义)  并且为了不和库的vector冲突,我们需要自己搞一个命名空间

这是我们要实现的大致框架

namespace ch{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin();iterator end();const_iterator begin()  const;const_iterator end() const;// construct and destroyvector();vector(size_t 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() const;void 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 = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};}

1.1 迭代器的获取

这一步很简单,只需要把我们的成员变量获取出来就可以,代码如下

iterator begin()
{return _start;
}iterator end()
{return _finish;
}
//迭代器(可读不可写)
typedef const T* const_iterator;const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}

1.2 构造函数和赋值重载 

1.2.1 无参构造函数

	//无参构造函数vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}

1.2.2  有参构造函数(对n个对象的去调用他们的构造)

vector(int n, const T& value = T()) 
{reserve(n); //已经知道多少空间,提前开,避免push_back多次开空间降低效率for (size_t i = 0; i < n; i++){push_back(value);}
}

缺省值T( ) ,这个地方的缺省值不能给0!!因为vector可能会存储内置类型,也可能会存储自定义类型,比如vector<string>,所以如果我们没给值,缺省值就要给他的默认无参构造函数,这个默认构造函数可以使用匿名对象

1.2.3 迭代器区间构造 

template<class InputIterator>
vector(InputIterator first, InputIterator last)
{//这里传的是别人的迭代器,不知道会传多少数据,不能提前扩容,只能让pushback的时候去判断while (first != last){push_back(*first);first++;}
}

类模板的本质上是为了让这个函数更加灵活,可以传不同类型的迭代器来帮助我们初始化,让其他的容器的迭代器来实现初始化

非法的间接寻址是为什么?

如下图我传(10,5),会出非法间接寻址

但是我传(10u,5)就可以正常使用了

 

 

vector(int n, const T& value = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(value);}
}

1.2.4 赋值重载 

vector<T>& operator= (vector<T> v)
{swap(v);return *this;
}

 1.3 析构函数

将_start指向的空间释放,成员变量全为空指针

vector()
{delete[] _start;_start = _finish = _endOfStorage = nullptr;
}

 1.4 常见接口

1.4.1 获取size和capacity

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

1.4.2 reserve提前扩容 

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < oldsize; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_endOfStorage = _start + n;}
}

1.4.3 resize 

有三种情况,第一种是给的n比原来的size小,第二种是n比size大但是比capacity小,第三种是n比capacity大,这个时候需要扩容

void resize(size_t n, const T& value = T())
{if (n <= size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}while (_finish != _start + n){*_finish = value;++_finish;}}
}

1.4.4 insert

iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endOfStorage){size_t n = pos - _start; //记录pos与起点位置的差值,开空间后,原pos迭代器失效,要更新size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + n; //更新新位置}iterator end = _finish - 1;while (end >= pos){*(end+1) = *(end);end++;}*pos = x;_finish++;return pos; //返回删除位置的下个元素位置,与文档相同
}

1.4.5 erase

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);size_t end = pos + 1;while (end != _finish){*(end - 1) = *(end);end++;}_finish--;
}

1.4.6 push_back()

我们在上面实现了insert 的实现,我们就复用insert来实现push_back()

void push_back(const T& x)
{insert(end(), x);
}

1.4.7 pop_back()

同上 

void pop_back()
{erase(--end());
}

1.4.8 swap 

用标准库的交换来实现

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}

 1.4.9 重载[ ]

T& operator[](size_t pos)
{return _start[pos];
}const T& operator[](size_t pos) const
{return _start[pos];
}

 1.5. 迭代器失效问题

会引起其底层空间改变的操作,都有可能使得迭代器失效,:resize、reserve、insert、assign、 push_back等。

1.5.1 insert的失效 

就是因为扩容导致pos失效,我们需要去及时更新pos

但是我们传的pos是值传递,所以我们更新的后pos更新,我们在后面解引用pos就会出现经典的解引用野指针问题。

就得用返回值传回pos 这也是为什么insert的返回值用iterator的原因,我们想继续用的话就得去接收一下返回值,就可以了

虽然有了返回值,我们可以去接收更新后的pos,但是一旦我们使用了任意一个可能扩容的函数,都会到时pos的失效,从而有可能回引发野指针问题,这个问题是不太好避免的,所以我们认为迭代器只能用一次,因为结果不可预测!

1.5.2 erase的失效 

           erase 删除 pos 位置元素后,pos 位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果 pos 刚好是最后一个元素,删完之后 pos 刚好是 end 的位置,而 end 位置是没有元素的,那么 pos 就失效了。因此删除 vector 中任意位置上元素时,vs 就认为该位置迭代器失效了。

结果是未定义的,不同编译器场景可能不同 

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。 

 二 vector实现的全部代码

#pragma once
#include<assert.h>
namespace ch
{template<class T>class vector{public:// Vector的迭代器是一个原生指针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(size_t n, const T& value = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(value);}}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}vector<T>& operator= (vector<T> v){swap(v);return *this;}~vector(){delete[] _start;_start = _finish = _endOfStorage = nullptr;}// capacitysize_t size() const{return _finish - _start;}size_t capacity() const{return _endOfStorage - _start;}void reserve(size_t n){if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < oldsize; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_endOfStorage = _start + n;}}void resize(size_t n, const T& value = T()){if (n <= size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}while (_finish != _start + n){*_finish = value;++_finish;}}}///access///T& operator[](size_t pos){return _start[pos];}///modify/void push_back(const T& x){insert(end(), x);}void pop_back(){erase(--end();}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endOfStorage){size_t n = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + n;}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);size_t end = pos + 1;while (end != _finish){*(end - 1) = *(end);end++;}_finish--;}private:iterator _start = nullptr;// 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};
}

总结

✨✨✨各位读友,本篇分享到内容是否更好的让你理解了C++的vector类,如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!。

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

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

相关文章

【UnityShader入门精要学习笔记】第十五章 使用噪声

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 使用噪声上…

亮相CCIG2024,合合信息文档解析技术破解大模型语料“饥荒”难题

近日&#xff0c;2024中国图象图形大会在古都西安盛大开幕。本届大会由中国图象图形学学会主办&#xff0c;空军军医大学、西安交通大学、西北工业大学承办&#xff0c;通过二十多场论坛、百余项成果&#xff0c;集中展示了生成式人工智能、大模型、机器学习、类脑计算等多个图…

Compose第一弹 可组合函数+Text

目标&#xff1a; 1.Compose是什么&#xff1f;有什么特征&#xff1f; 2.Compose的文本控件 一、Compose是什么&#xff1f; Jetpack Compose 是用于构建原生 Android 界面的新工具包。 Compose特征&#xff1a; 1&#xff09;声明式UI&#xff1a;使用声明性的函数构建一…

opencascade 快速显示AIS_ConnectedInteractive源码学习

AIS_ConcentricRelation typedef PrsDim_ConcentricRelation AIS_ConcentricRelation AIS_ConnectedInteractive 简介 创建一个任意位置的另一个交互对象实例作为参考。这允许您使用连接的交互对象&#xff0c;而无需重新计算其表示、选择或图形结构。这些属性是从您的参考对…

蓝桥杯嵌入式国赛笔记(4):多路AD采集

1、前言 蓝桥杯的国赛会遇到多路AD采集的情况&#xff0c;这时候之前的单路采集的方式就不可用了&#xff0c;下面介绍两种多路采集的方式。 以第13届国赛为例 2、方法一&#xff08;配置通道&#xff09; 2.1 使用CubeMx配置 设置IN13与IN17为Single-ended 在Parameter S…

今日好料推荐(大数据湖体系规划)

今日好料推荐&#xff08;大数据湖体系规划&#xff09; 参考资料在文末获取&#xff0c;关注我&#xff0c;获取优质资源。 大数据湖体系规划 一、大数据湖简介 大数据湖&#xff08;Data Lake&#xff09;是一个集中式的存储库&#xff0c;用于存储来自各种来源的结构化和…

蓝桥杯杨辉三角

PREV-282 杨辉三角形【第十二届】【蓝桥杯省赛】【B组】 &#xff08;二分查找 递推&#xff09;&#xff1a; 解析&#xff1a; 1.杨辉三角具有对称性&#xff1a; 2.杨辉三角具有一定规律 通过观察发现&#xff0c;第一次出现的地方一定在左部靠右的位置&#xff0c;所以从…

快速下载极客时间课程

仅供学习&#xff0c;切勿商用 1. 下载 下载geektime-downloader&#xff0c;安装到指定文件夹&#xff0c;注意路径尽量不要出现汉字 不想去github上下载的可以直接下载文章顶部的软件安装包。 2. 执行命令 在安装geektime-downloader目录下&#xff0c;点击鼠标右键&…

Spring和Servlet的整合

Servlet对象是谁创建的&#xff1f; 由服务器端创建的 程序启动调用加载spring配置文件代码 Web应用程序启动也需要加载Spring配置文件 Web开发中有三大组件&#xff1a; 1、servlet 2、filter 3、listener&#xff08;request&#xff0c;session&#xff0c;application&…

在docker中运行SLAM十四讲程序

《十四讲》的示例程序依赖比较多&#xff0c;而且系统有点旧。可以在容器中运行。 拉取镜像 docker pull ddhogan/slambook:v0.1这个docker对应的github&#xff1a;HomeLH/slambook2-docker 拉下来之后&#xff0c;假如是Windows系统&#xff0c;需要使用XLaunch用于提供X11…

无人机操作界面来了,起点就很高呀。

无人机操作界面设计需要考虑以下几个方面&#xff1a; 易用性&#xff1a;无人机操作界面应该简单直观&#xff0c;易于操作和理解。操作按钮和控键应该布局合理&#xff0c;易于触摸或点击。重要的操作功能应该易于找到和使用&#xff0c;避免用户迷失或困惑。实时反馈&#…

jupyter notebook更改位置

1.找到jupyer的配置文件 一般在c盘用户的.jupter文件夹下 2. 用记事本打开这个配置文件&#xff0c;定位到c.NotebookApp.notebook_dir /path_to_your_directory 替换你的位置 3.找到jupyer图标的位置&#xff0c;打开属性 添加要存放的位置在目标文件的末尾&#xff0c;重新…

9.3 Go语言入门(变量声明和函数调用)

Go语言入门&#xff08;变量声明和函数调用&#xff09; 目录二、变量声明和函数调用1. 变量声明1.1 使用 var 关键字声明1.2 简短声明1.3 零值1.4 常量 2. 函数调用2.1 函数定义2.2 多个返回值2.3 命名返回值2.4 可变参数2.5 匿名函数和闭包 目录 Go 语言&#xff08;Golang&a…

Unity射击游戏开发教程:(28)敌人被摧毁时掉落的能量提升

在这篇文章中,我将介绍如何在敌人被摧毁时产生能量提升。 首先,有一个生成管理器,负责生成敌人和能量提升。我正在对其进行转换,以便当敌人被摧毁时,有可能会掉落能量。本文将仅介绍当敌人被摧毁时掉落的能量道具。我将介绍为电源添加一个平衡的生成系统。 Spawn Manager…

XXE漏洞详解——进阶篇

读取文件时有特殊符号 在读取文件时&#xff0c;文件中包含"<,>,&"等这些特殊符号时&#xff0c;会被xml解析器解析&#xff0c;报错从而导致读取失败&#xff0c;例如尝试读取以下文件 C:\test.txt 内容&#xff1a; <Baize Sec> payload: <…

内网安全--隧道技术-MSF上线本地

免责声明:本文仅做技术交流与学习... 不得不说,小白最近也是用上了viper,这里要特别感谢一下my bro 北岭敲键盘的荒漠猫 MSF--viper: --生成马子-->上线 --进入meterpreter. 1-查看路由,添加路由. 查看路由信息 : run autoroute -p run post/multi/manage/autoroute 添加…

登峰造极,北斗相伴——纪念人类首次登顶珠穆朗玛峰71周年

71年前的今天&#xff0c;1953年5月29日11时30分&#xff0c;人类实现了一个伟大的壮举&#xff1a;首次登上了珠穆朗玛峰&#xff0c;这座海拔8848.86米的世界最高峰。这是一次充满了艰辛、勇气和智慧的探险&#xff0c;也是一次改变了人类历史和文化的探险。 自那以后&#…

Java集合—TreeSet和TreeMap

一、TreeSet 1.当使用无参构造器&#xff0c;创建TreeSet时&#xff0c;仍然是无序的。 2.若希望添加的元素有序&#xff0c;需要使用TreeSet提供的构造器,传入一个比较器。 该比较器是一个接口&#xff0c;里面有一个方法叫compare()&#xff0c;传入一个实现该接口的类(匿名内…

Linux文件管理

Linux系统中&#xff0c;文件以树状图形式存储&#xff0c;即单根文件系统&#xff0c;以用户为分支分别存储文件。 文件操作 相对路径表示方法&#xff0c;.当前目录&#xff0c;..上层目录&#xff0c;~家目录&#xff0c;也可以使用绝对路径/的表示方法&#xff0c;其他常…

面试问到Spring中的@Autowired注解,可以这样答

前言 在Spring框架中&#xff0c;依赖注入是一个核心概念&#xff0c;它允许将一个对象的依赖关系外部化并由Spring容器来管理。Autowired注解是实现这一点的关键工具之一。当然&#xff0c;这块知识也是面试官们老生常谈的问题。 下面就跟着博主的步伐&#xff0c;一起来探讨…