C++标准模板库——vector的使用及其模拟实现

目录

一. vector的介绍
1.vector的介绍

二.vector的使用

  1. vector中常见接口的介绍
  2. vector的构造和析构函数
  3. vector的三种遍历方式

三.vector的模拟实现

  1. vector的增删查改
  2. vector容器的容量变化和大小增减
  3. vector迭代器失效问题
  4. vector的小框架 构造函数和析构函数
  5. 迭代器和operator[]的实现
  6. vector的拷贝构造函数和operator=
  7. memcpy拷贝问题
  8. 模拟实现整体源代码

一.vector的介绍

1.vector的介绍

更多详细细节可以参考 vector参考书
在这里插入图片描述

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小
    为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

二. vector的使用

1. vector的常见接口介绍

1.1vector的定义

在这里插入图片描述

1.2 vector 迭代器 的使用

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

1.3 vector的空间增长问题

在这里插入图片描述
测试代码:

#include<iostream>
#include<vector>
using namespace std;void Test_vector2()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{Test_vector2();return 0;
}

运行结果:
在这里插入图片描述

vs:运行结果:vs下使用的STL基本是按照1.5倍方式扩容

通过reserve()接口提前设置好容量,我们可以减少扩容次数,减少扩容对效率的损耗

测试代码:

#include<iostream>
#include<vector>
using namespace std;void Test_vector1()
{vector<int> v;for (size_t i = 0; i < 10; i++){v.push_back(i);}}void Test_vector2()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}void Test_vector3()
{vector<int> v;size_t sz = v.capacity();v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容cout << "making bar grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{Test_vector3();return 0;
}

在这里插入图片描述

1.4vector的增删查改

在这里插入图片描述
测试代码:

void Test_vector1()
{vector<int> v;for (size_t i = 0; i < 10; i++){v.push_back(i);}cout << "尾插数据后:" << endl;for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";//像数组一样用[]访问vector中的元素}cout << endl;v.pop_back();v.pop_back();cout << "尾删两个数据后:" << endl;for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector<int>::iterator it = v.begin();v.insert(it+3, 30);//在下标为3的前面插入一个30for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;it = v.erase(it+3);//删除it位置的数,并把该位置被删除的后面的数的迭代器返回给itfor (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;
}

运行结果:
在这里插入图片描述

2.vector的构造函数和析构函数

在这里插入图片描述

在这里插入图片描述

3.vector的三种遍历方式

测试代码:

void Test_vector4()
{vector<int> v;for (size_t i = 0; i < 10; i++){v.push_back(i);}cout << "第一种用[]进行遍历:" << endl;for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;cout << "第二种用迭代器进行遍历:" << endl;vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;cout << "第三种用范围for进行遍历:" << endl;for (auto e : v){cout << e << " ";}cout << endl;
}

运行结果:
在这里插入图片描述

三.vector的模拟实现

开始时定义个自己的命名空间,然后利用类模板控制vector可以存放多种类型的数据,定义迭代器和私有成员变量,_start相当于vector常用接口中的begin(),_finish相当于end(),_endofstorage相当于_start+capacity();
在这里插入图片描述

#pragma once#include<assert.h>
#include<string.h>
#include<iostream>
using namespace std;
namespace  simulation
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start;iterator _finish;iterator _endofstorage;};
}

1. vector的增删查改

代码:

//尾插
void  push_back(const T& x)
{if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);			}*_finish = x;_finish++;
}//在pos位置前插入x
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;}//删除pos位置的元素
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos <= _finish);iterator it = pos+1;while (it<_finish){*(it-1) = *it;++it;}_finish--;return pos;
}

测试代码:

void test_vector2()
{simulation::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);v.push_back(7);v.push_back(8);for (int e : v){cout << e << " ";}cout << endl;//在4前面插入一个40v.insert(v.begin() + 3, 40);for (int e : v){cout << e << " ";}cout << endl;//头插一个100v.insert(v.begin(), 100);for (int e : v){cout << e << " ";}cout << endl;//尾插1000v.insert(v.end(), 1000);for (int e : v){cout << e << " ";}cout << endl;//头删v.erase(v.begin());for (int e : v){cout << e << " ";}cout << endl;//尾删v.erase(v.end() - 1);for (int e : v){cout << e << " ";}cout << endl;
}

运行结果:
在这里插入图片描述

  1. vector容器的容量变化和大小增减

成员函数实现:

void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);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 <= _finish){_finish = _start + n;}else{reserve(n);while (n + _start > _finish){*_finish = val;_finish++;}}}

在这里插入图片描述

  1. vector迭代器失效问题
    在这里插入图片描述
    在pos位置插入数据时,可能存在扩容问题,扩容一般情况下都会找一块新的空间,然后释放旧空间,把指针指向新的空间,但是此时传进去的参数pos没有跟着空间的变化而与_start发生相对变化,所以这时迭代器就失效了,不能再让迭代器往后使用了,此时想要不让迭代器失效只能保留pos与_start的偏移量,然后等到扩容后把pos位置的迭代器更新,那么pos位置的迭代器才能继续使用。

在这里插入图片描述
erase这个接口也会使得迭代器失效,原因在于删除pos位置的数据之后,pos指向的数据的值已经被删除,所以想要继续使用迭代器需要在该接口返回值那里返回一个迭代器,指向被删除的值的下一个位置的迭代器,方能继续使用.

所以这样实现才是合理的

//删除pos位置的元素
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos <= _finish);iterator it = pos+1;while (it<_finish){*(it-1) = *it;++it;}_finish--;return pos;
}
  1. vector的小框架 构造函数和析构函数
    代码实现:
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}vector(int n, const T& val = T())
{reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}vector():_start(nullptr),_finish(nullptr),_endofstorage(nullptr)
{}vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{reserve(v.capacity());for (auto& e : v){push_back(e);}
}vector<T>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}
  1. 迭代器和operator[]的实现
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;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}
  1. vector的拷贝构造函数和operator=
vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{reserve(v.capacity());for (auto& e : v){push_back(e);}
}vector<T>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}
  1. memcpy拷贝问题
    memcpy进行拷贝时会对自定义数据类型的数据进行浅拷贝,而浅拷贝在进行析构的时候,会进行两次析构,导致出现错误,所以拷贝的时候应该进行深拷贝
    如下是代码实现
void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}

通过for循环一份一份的拷贝到新的空间就不会造成空间被释放两次

  1. 模拟实现整体源代码
#pragma once#include<assert.h>
#include<string.h>
#include<iostream>
using namespace std;
namespace  simulation
{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;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(v.capacity());for (auto& e : v){push_back(e);}}vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);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 <= _finish){_finish = _start + n;}else{reserve(n);while (n + _start > _finish){*_finish = val;_finish++;}}}void  push_back(const T& x){if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;_finish++;}void insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;}iterator erase(iterator pos){assert(pos >= _start);assert(pos <= _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;++it;}_finish--;return pos;}private:iterator _start;iterator _finish;iterator _endofstorage;};
}

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

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

相关文章

有什么推荐使用的企业上网行为管理软件?

在当今信息化社会&#xff0c;企业的上网行为管理越来越重要。企业上网行为软件是一种能够监控和管理企业员工上网行为的工具&#xff0c;它可以帮助企业更好地管理网络资源&#xff0c;提高工作效率&#xff0c;保护企业信息安全&#xff0c;并符合相关的法律法规。本文将深入…

尝试访问启动磁盘设置时出错怎么办?

当出现“尝试访问启动磁盘设置时出错”这样的错误提示&#xff0c;而且启动转换控制面板打不开了时&#xff0c;是无法开启触摸板功能的。我们可以使用以下方法来解决问题。 1. 在Windows桌面左下角搜索框输入“计算机管理”后点击“打开”。 2. 点击“本地用户与组”&#xff…

[论文阅读]A ConvNet for the 2020s

摘要 视觉识别的咆哮的20年代开始于ViTs的引入&#xff0c;它很快取代了卷积神经网络&#xff0c;成为最先进的图像分类模型。另一方面&#xff0c;一个原始的ViT在用于一般的比如目标识别和语义分割的计算机视觉任务的时候面临困难。层次Transformer(例如&#xff0c;Swin-Tr…

新手小白如何入门学习CTF?【网络安全】

最近有很多新手小白私信我&#xff1a;如何学习CTF&#xff1f;新手小白应该怎么入门CTF&#xff1f;想打CTF&#xff0c;但是没有思路怎么办&#xff1f; 昨天下班之后&#xff0c;花了几个小时&#xff0c;整理了一下CTF学习的思路与方法&#xff0c;分享给大家&#xff0c;如…

十一、流程控制-if-switch

流程控制 1.流程控制1.1.复合语句1.2.if语句★1.2.1.简单条件的if语句★1.2.2.if...else语句★1.2.3.if...else if多分支语句★1.2.4.if语句的嵌套★ 1.3.switch多分支语句★1.3.1.switch语句通用语法★1.3.2.switch表达式★训练一★训练二★ ————————————————…

Linux 安装 git

一 . 安装git 方式1&#xff1a;通过yum 安装 yum -y install git查看是否安装成功 git --version安装目录在&#xff1a;/usr/libexec/git-core yum 安装有一些缺点 &#xff1a;不能自己指定安装目录、安装版本 方式 2 下载tar.gz 包 配置 查看git 版本&#xff1a;Index…

KF32A学习笔记(一):工程导入、编译烧录方法(KF32 IDE+ KF32 PRO)

目录 概述KF32 IDE打开现有项目工程1.工程导入2.编译工程3.下载程序 KF32 PRO 概述 本文主要是对KF32A150芯片程序的编译、烧录方法进行说明。针对开发过程中的编译烧录和无代码情况下的烧录两种场景&#xff0c;需要安装ChipON PRO KF32和ChipON IDE KF32两个上位机工具&…

【面试经典150 | 数组】跳跃游戏 II

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;贪心 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容…

FBX文件结构解读【文本格式】

FBX 格式几乎受到所有 3D 引擎的支持&#xff0c;是 Autodesk 开发的 3D 模型的专有格式。它支持顶点、索引、法线、UV坐标、材质和动画。 FBX还支持许多其他类型的信息&#xff0c;但它们对游戏引擎几乎没有用处。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 有两种…

9.基于粤嵌gec6818开发板小游戏2048的算法实现

2048源码&#xff1a; 感兴趣的可以去了解一下2048优化算法&#xff1a; 基于蒙特卡罗树搜索的_2048_游戏优化算法_刘子正 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #incl…

程序员自由创业周记#12:999%

下载量涨了999% 在此之前&#xff0c;我的Apple开发账号上只有一个产品-学伟扫描&#xff0c;因为没有推广&#xff0c;只靠自然流量&#xff0c;每天的下载量寥寥无几&#xff0c;这种稳定无人问津的状态断断续续保持了4年。 之前的周记里介绍了最近在做创业的第二个项目-学伟…

PHP 如何创建一个 composer 包 并在 项目中使用自己的 composer sdk 包

第一步创建一个composer SDK项目 创建一个 composer.json文件或使用 命令 &#xff08;如果不清楚怎么弄 直接跳过即可&#xff0c;一般都会默认配置&#xff09; composer init这是生成的composer.json文件 将自己要使用的包添加到 require 中&#xff0c;如果没有require则…

mac安装运行superset及踩坑解决过程

介绍 Apache Superset 是一个现代的企业级商业智能 Web 应用程序。它快速、轻量、直观&#xff0c;并加载了各种选项&#xff0c;使所有技能的用户都可以轻松探索和可视化他们的数据&#xff0c;从简单的饼图到高度详细的 Deck.gl 地理空间图表。 安装 首先安装自制homebrew安…

1. PCIE基础入门知识

GT/s 是"每秒十亿次传输" 时间版本速率2003Pcie 1.02.5GT/s2006PCIE 2.05GT/s2010PCIE 3.08GT/s2017PCIE 4.016GT/s 高速接口&#xff1a;雷电接口&#xff08;PCIEx4接口 &#xff09; PCIE接口 工作模式&#xff1a;Endpoint 和 Root Port。 Endpoint&#xff0…

Si314 低功耗 14 通道电容触摸传感器,软硬件兼容替代GTX314L

Si314 是一款具有自动灵敏度校准功能的 14 通道电容传感器&#xff0c;其工作电压范围为 1.8~5.5V。 Si314 设置休眠模式来节省功耗&#xff0c;此时&#xff0c;功耗电流为 10uA3.3V。Si314 各个感应通道可实现独立使能、校准、灵敏度调节&#xff0c;可以确保可靠性&#xff…

Terminnal will be login out after 20 second

锐捷交换机&#xff0c;命令敲着敲着 &#xff0c;就提示20秒后将中断 &#xff0c;show ip ssh 查看也一下也没有什么特殊的。 于是查看了一下VTY下的配置 absolute-timeout 5 ,这句话是什么意思呢 &#xff1f; 5分钟强制退出 &#xff01; 改进方法&#xff1a; (config)#…

14:00面试,14:06就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到5月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Revopoint的3D输出格式及转换工具

在 CES 展会期间&#xff0c;许多参观者向我们询问与我们的 3D 扫描仪相关的问题。 最常见的问题包括我们的扫描仪导出的文件格式&#xff0c;以及该文件是否与 3D 打印机兼容&#xff1f; 因此&#xff0c;我们决定回答这些问题&#xff0c;并在本文中对常见的 3D 文件格式进行…

vuejs - - - - - 使用code编辑器codemirror

使用code编辑器codemirror 0. 效果图1. 依赖安装2. 组件封装3. 组件使用 0. 效果图 列表实现参考: 列表实现代码 1. 依赖安装 npm install codemirror codemirror-editor-vue3 jsonlint-mod 2. 组件封装 code-mirror-editor.vue <template><VueCodeMirrorclas…

Nginx location 精准匹配URL = /

Location是什么&#xff1f; Location是Nginx中的块级指令(block directive)&#xff0c;通过配置Location指令块&#xff0c;可以决定客户端发过来的请求URI如何处理&#xff08;是映射到本地文件还是转发出去&#xff09;及被哪个location处理。 匹配模式 分为两种模式&…