【C++】详解STL容器之一的 vector

目录

概述

迭代器

数据结构

优点和缺点

接口介绍

begin

end

rbegin

rend

resize

reseve

insert

erase

其他一些接口

模拟实现

框架

获取迭代器

深浅拷贝

赋值重载

reseve

resize

拷贝构造

构造

析构

insert

erase

其他


概述

vector是STL的容器之一。vector的底层结构类似于数组——在内存中开辟一块连续的空间。与数组不同的是vector可以动态的改变空间的大小(扩容或缩容)。

vector一般不会缩容,而是会经常的扩容——扩容的大小总比用户需要的多,这和vector的扩容机制有关。

vector不支持原地扩容,会新开辟一块更大的空间。将旧空间的值浅拷贝给新空间,然后释放旧空间。vector的空间的动态改变是有代价的,尤其是数据量特别大时,不会轻易缩容。

不同平台的扩容原则是一样的,但扩容的细节并不相同。VS下是根据旧空间的1.5倍扩容,g++是根据旧空间的2倍扩容


迭代器

vector的迭代器不需要像list的那样把普通指针进行封装,详见:http://t.csdnimg.cn/76tVf

因为vector维护的是一段线性空间,它的底层是一块连续的空间。普通的指针刚好能完成数据的随机访问,空间的遍历,数据的存取等。下面是迭代器的源码定义。

templat <class T, class Alloc = alloc>
class vector
{public:
typedef T value_type;
typedef value_type* iterator; //......
}

 T*就是vector的迭代器。如果vector存的是int类型的数据,vector的迭代器就是int*类型的指针。存的如果是string类(自定义类型)类型的数据,vector的迭代器就是string*类型的指针。

迭代器失效

扩容会引发迭代器失效。首先获取了一个空间的迭代器,然后这个空间发生了扩容,此时这个迭代器是指向旧空间的,旧空间会被归还给操作系统。这种情景下的迭代器失效可以理解为野指针问题。VS环境下会强制报错。如下图

数据结构

vector管理线性空间用了三个迭代器,如下是源代码定义

templat <class T, class Alloc = alloc>
class vector
{
//......
protected:
iterator start;
iterator finish;
iterator end_of_storage;
//......
}

start——指向空间的开头

finish——指向有效数据的下一个位置

end_of_storage——指向有效空间的下一个位置


优点和缺点

优点支持随机访问,排序消耗的时间复杂度比其他容器低

有如下代码,验证三大容器vector, list, deque,在相同数据量,相同数据,数据的顺序也相同的情况下,用vector容器的排序有多大优势。在release环境下验证

#include<vector>
#include<list>
#include<deque>
#include<algorithm>
#include<time.h>
#include<iostream>void Test()
{
//数据量int N = 100000;//十万//int N = 1000000;//百万//int N = 10000000;//千万//int N = 100000000;//一亿  std::vector<int> v; //三大容器std::list<int> l;std::deque<int>d;for (int i = N; i > 0; i--) //插入相同的数据,数据顺序也相同{int e = rand();    v.push_back(e);l.push_back(e);d.push_back(e);};clock_t begin1 = clock(); //时间函数sort(v.begin(), v.end()); clock_t end1 = clock(); clock_t begin2 = clock(); l.sort();clock_t end2 = clock(); clock_t begin3 = clock(); sort(d.begin(), d.end());    clock_t end3 = clock(); printf("vector的用时是%d毫秒\n", end1 - begin1); printf("list的用时是%d毫秒\n", end2 - begin2); printf("deque的用时是%d毫秒\n", end3 - begin3);}int main()
{Test();return 0;
}

结果展示

即使排了一亿个数据,快排加vector容器也只用了4秒。list的底层是链表用了将近两分钟,效率低下。deque是一个类似于vector和list的结合体的容器,用了20秒。vector最引以为豪的优势——排序的效率高。原因在于vector的底层是连续的空间,支持随机访问,只需O(1)复杂度便可访问任意位置的数据。

在空间足够的情况下,尾部插入,尾部删除的效率为O(1)

缺点头部插入,头部删除,随机位置插入,随机位置删除,效率为O(N)。原因很简单:这些操作都需要挪动数据。如果数据量大,并且要频繁的头插头删,这便是堪比一个O(N^2)的算法。在标准库的接口中,并没有直接给头插,头删的接口。


接口介绍

begin

获取第一个数据位置的迭代器或const迭代器

end

获取最后一个有效数据的下一个位置的迭代器或const迭代器

rbegin

获取最后一个有效数据的迭代器或const迭代器

rend

获取第一个有效数据的前一个位置的迭代器

resize

改变vector容器有效数据的个数,改变为n个数据。如果n小于有效空间,删数据。n大于有效空间,扩容,并把多余的有效数据初始化为val

reseve

改变容器的有效空间。n小于有效空间时,不做处理。n大于有效空间时,把空间开至n或更大。

insert

在迭代器position之前插入val

在迭代器position之前插入n个val
在迭代器position之前插入迭代器区间first到last的元素

erase

删除迭代器position位置的元素,或删除迭代器区间first到last的元素

其他一些接口

size
获取数据个数
capacity获取容量大小
empty判断是否为空
push_back
尾插
pop_back 尾删
operator[] 
 像数组一样访问

模拟实现

框架

namespace bit
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

获取迭代器

iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

深浅拷贝

过vector容器存的是自定义类型,它们的数据可能会指向某一块空间。当我们想要新的vector容器拷贝旧的vector容器数据时,新的vector容器是否要额外开空间储存自定义类型指向的空间的数据,便涉及深浅拷贝问题。

如下示意图

上文已经提到扩容时是浅拷贝,当 vector 需要扩容时,它会分配一个新的更大的内存块,然后将原来的元素拷贝到新的内存中,并释放原来的内存。这确保了在扩容时,原有元素的地址不会改变,从而避免了深拷贝的开销

而在拷贝构造函数和赋值运算符中,vector 会执行深拷贝,即它会复制其中的每个元素,而不是简单地复制指向内存的指针。这样,每个vector 对象都有自己独立的内存存储其元素,互不影响,也避免了浅拷贝可能带来的问题。

有了上述了解,模拟实现一下赋值重载

赋值重载

现在写法

void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}vector<T>& operator=(vector<T> v){swap(v);return *this;}

reseve

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;}}

resize

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

拷贝构造

vector(const vector<T>& v){_start = new T[v.capacity()];//memcpy(_start, v._start, sizeof(T)*v.size());for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i]; //赋值重载}_finish = _start + v.size();_endofstorage = _start + v.capacity();}

构造

vector(size_t n, const T& val = T()){resize(n, val);}

析构

~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}

insert

iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);// 解决pos迭代器失效问题pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}

erase

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

其他

void push_back(const T& x){insert(end(), x);}void pop_back(){erase(--end());}size_t capacity() const{return _endofstorage - _start;}size_t size() const{return _finish - _start;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

本篇内容就到这里啦

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

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

相关文章

连接docker中的MySQL出现2058错误

出错场景&#xff1a;在虚拟机中用docker技术下载最新版本的MySQL&#xff0c;在本地电脑上连接发现出现2058错误。 解决方法&#xff1a; 按照以下步骤 1. 2. ALTER USER root% IDENTIFIED WITH mysql_native_password BY 自己MySQL的密码; 3.成功

redis--安装

简介 官网&#xff1a;RedisInsight - The Best Redis GUI 各个版本官网下载地址&#xff1a;http://download.redis.io/releases/ Redis和Memcached是非关系型数据库也称为NoSQL数据库&#xff0c;MySQL、Mariadb、SQL Server、PostgreSQL Oracle 数据库属于关系型数据 应用…

为什么选择ATECLOUD自动化测试平台?

在当今飞速发展的时代&#xff0c;一切都在不断进步与变革&#xff0c;电测行业也由手动测试逐步转向了自动化测试。但是随着科技的发展&#xff0c;对于产品的测试要求也越来越高&#xff0c;传统的自动化测试系统已经无法满足用户日益增长的测试需求&#xff0c;全新的ATE测试…

优化理论复习——(四)

无约束优化专题&#xff0c;主要使用了序列无约束极小化方法 无约束优化问题相关解法 最优性条件 互补松弛条件 对于一般约束优化问题&#xff1a; 整理一下就是著名的kkt条件&#xff1a; 这里只需要注意一点&#xff0c;那就是互补松弛条件只对不等式约束有限制。 然后是…

在全志H616核桃派开发板上实现超声波传感器测距

前言​ 超声波传感器是一款测量距离的传感器。其原理是利用声波在遇到障碍物反射接收结合声波在空气中传播的速度计算的得出。在测量、避障小车&#xff0c;无人驾驶等领域都有相关应用。 实验目的​ 通过python编程实现超声波传感器测距。 实验讲解​ 下图是一款市面上常…

【智能算法】雪消融优化算法(SAO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年&#xff0c;L Deng受到雪升华和融化行为启发&#xff0c;提出了雪消融优化算法&#xff08;Snow Ablation Optimizer, SAO&#xff09;。 2.算法原理 2.1算法思想 SAO模拟了雪的…

C语言,实现数字谱到简谱的转换(二)

C语言&#xff0c;实现数字谱到简谱的转换&#xff08;二&#xff09; 前言&#xff1a;本文初编辑于2024年5月8日 CSDN&#xff1a;https://blog.csdn.net/rvdgdsva 博客园&#xff1a;https://www.cnblogs.com/hassle 前言 结合前文使用 之前的程序默认C调4/4拍&#xff…

探索DeepSeek平台:新一代MoE模型的深度体验

简介 DeepSeek是一个创新的人工智能平台&#xff0c;它最近推出了其最新版本的模型——DeepSeek-V2 MoE&#xff08;Mixture of Experts&#xff09;。这个平台不仅提供了一个交互式的聊天界面&#xff0c;还提供了API接口&#xff0c;让用户可以更深入地体验和利用这一先进的…

使用SpringBoot+Redis做一个排行榜【推荐】

SpringBoot Redis实现排行榜 一、Zset有序集合介绍 Zset是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分( score) ,这个评分( score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的&#xff0c;但是评分可以是重复了…

Service 和 Ingress

文章目录 Service 和 IngressServiceEndpointservice 的定义代理集群外部服务反向代理外部域名Service 常用类型 IngressIngress-nginx安装使用 Service 和 Ingress service 和 ingress 是kubernetes 中用来转发网络请求的两个服务&#xff0c;两个服务用处不同&#xff0c;se…

去哪儿前端一面

谈谈你对mvvm的理解 MVVM分为三个部分:分别是M(Model,模型层 ),V(View,视图层),VM(ViewModel,V与M连接的桥梁,也可以看作为控制器) 1、 M:模型层,主要负责业务数据相关; 2、 V:视图层,顾名思义,负视图相关,细分下来就是html+css层; 3、 VM:V与M沟通的…

数据结构学习——二叉树

1. 树概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结点&…

用户下单操作

一&#xff1a;用户下单需求分析和设计&#xff1a; 用户下单业务说明&#xff1a; 在电商系统中&#xff0c;用户是通过下单的方式通知商家&#xff0c;用户已经购买了商品&#xff0c;需要商家进行备货和发货。 用户下单后会产生订单相关数据&#xff0c;订单数据需要能够体…

如何使用多协议视频汇聚/视频安防系统EasyCVR搭建智慧园区视频管理平台?

智慧园区作为现代化城市发展的重要组成部分&#xff0c;不仅承载着产业升级的使命&#xff0c;更是智慧城市建设的重要体现。随着产业园区竞争的逐渐白热化&#xff0c;将项目打造成完善的智慧园区是越来越多用户关注的内容。 然而我们往往在规划前期就开始面临众多难题&#…

保护公司机密:避免员工带着数据说拜拜

公司的核心资产之一就是数据。无论是客户信息、研发代码、内部决议、财务报告、商业合同、设计图纸等都是公司的重要资产。如果这些数据在员工离职时被带走&#xff0c;或在员工在职期间不当行为导致数据泄露&#xff0c;将给公司带来重大损失。 然而&#xff0c;保护这些数据…

软考143-下午题-【试题二】:E-R图、关系模式

一、分值与目标 15分&#xff0c;目标10 二、题目形式 示例&#xff1a; 三、E-R图的基本图形元素 示例&#xff1a; 3-1、实体 1、弱实体 在现实世界中有一种特殊的联系&#xff0c;这种联系代表实体间的所有 (Ownership) 关系&#xff0c;例如&#xff1a;职工与家属的联系…

STC8增强型单片机开发 【第一个程序 - 点亮第一盏灯】

目录 一、创建项目 1. 创建一个新的项目 ​编辑 2. 配置开发板信息 ​编辑 3. 取消汇编配置 4. 项目结构 二、编码实现 1. 项目准备 2. 代码实现 点灯&#xff1a; 熄灯&#xff1a; 3. 编译烧录运行 配置编译输出 保存和编译代码 ​编辑 烧录 一、创建项目 1. …

【Arduino IDE 2】Windows平台安装ESP8266 NodeMCU LittleFS Uploader(文件上传插件)

在Arduino IDE 2&#xff08;2.2.1或更高版本&#xff09;上&#xff0c;如何安装基于ESP8266 NodeMCU的LittleFS文件系统上传插件&#xff0c;以及如何将文件上传到ESP8266 NodeMCU板文件系统。 一、LittleFS简介 LittleFS是一个为微控制器创建的轻量级文件系统&#xff0c;可…

实验五 Spark Structured Streaming编程实践

一、编写程序 (1). 按照tag分组统计生成的日志数。 在新开的终端内输入 vi spark_exercise_testsyslog1.py &#xff0c;贴入如下代码并运行。运行之前需要关闭“tail终端”内的tail命令并重新运行tail命令&#xff0c;否则多次运行测试可能导致没有新数据生成。 #!/usr/bin…

onlyoffice容器打包成镜像

书接上篇&#xff0c;onlyoffice容器已经更改在本地docker环境中了&#xff0c;之后需要部署到测试环境的docker中&#xff0c;采用容器打包成本地镜像 1、本地docker 查看容器&#xff1a;docker ps 生成镜像&#xff1a;docker commit -p blissful_lichterman 重命名镜像&a…