前言

vector是变长数组,有点像数据结构中的顺序表,它和list也是经常被拿出作对比的, vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小,如果扩容,因为要开一个新数组把旧元素挪过来,消耗很大,所以扩容一般是扩二倍或者1.5倍。

vector的模拟也可以像string一样来实现,但是SGI下源码用的是三个迭代器,后面会具体讲。


一、vector类以及常用接口和函数

1.构造函数

和string基本一样

2.迭代器

发现也一样,因为双向迭代器在C++11后都是四个迭代器,具体使用也没啥说的,简单示范一下,遍历容器:

vector<int> v = {1,2,3,4};
vector<int>::iterator it = v.begin();
while (it != v.end())
{cout << *it << ' ';it++;
}

有模板以后,类名不再是类型名,vector< T >才是类型名

3.容量方面

在这里插入图片描述
和string基本一样。
vs下是1.5倍扩容,而g++下是2倍扩容,vs是PJ版本STL,g++是SGI版本STL。

4.重要函数

在这里插入图片描述
一眼瞅过去和string基本一样,这里emplace系列在C++11中已经讲完了不再提。主要改变是insert和erase,从string之后,所有的insert和erase基本上都只支持迭代器插入/删除。
在这里插入图片描述
在这里插入图片描述
返回值也是迭代器,解决迭代器失效的问题,后面谈。

5.二维数组

vector<vector<int>> vv;//这就是一个二维数组。每一维放的是vector<int>

二、迭代器失效问题

想知道这个先知道一个问题:vector的迭代器是什么样的?和string类似,

typedef T* iterator;
typedef const T* const_iterator;

先看迭代器失效的一些场景:

erase掉偶数

vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
while (it != v.end())
{if (*it % 2 == 0){v.erase(it);}++it;
}

在一个位置反复insert

vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
int n = 10;
while (n--)
{v.insert(it, 10);
}

这两份代码在vs下都崩溃了。
只要涉及到底层空间的改变都有可能会导致迭代器失效。
为什么会失效呢?
以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,下一步使用时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
关于erase

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

Linux下检查和处理都没有vs更加严格和极端,可能不会崩溃,但是结果都是未定义的,所以我们要学会正确的使用迭代器。
解决方案:
库里的实现均带有返回值,返回最新位置的迭代器,想上面那么使用就可以拿it重新接受。

it = v.erase(it);
it = v.insert(it,30);

三、模拟实现

SGI下没有使用像string一样的 T * arr和size和capacity,他使用了三个迭代器(T*类型的三个指针),这样处理更加的方便,扩容方面,指针的计算也更有效率。
_start --充当T * arr;
_finish size()
_end_of_storage capacity();
构造:
在这里插入图片描述
push_back
在这里插入图片描述
看完这个push_back()就会明白很多,if判断是判断size等不等于capacity,++finish相当于++_size。
rerserve:
在这里插入图片描述
这个更加清晰,n是新的capacity的大小,start指向开始,finish指向tmp+size(),end_of_storage指向空间的结束。
OK,知道这些就好自己手搓了。

size和capacity

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

reserve

void reserve(size_t n)
{if (capacity() < n){size_t sz = size();//注意保存一下size,因为后面还需要计算finishT* tmp = new T[n];if (_start){for (size_t i = 0; i < sz; ++i){tmp[i] = _start[i];}//memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}

迭代器:

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

insert:

后面的容器基本上都是写完insert之后复用insert,比如push_back,就是insert(end(),x);
insert的注意事项:就是扩容之后迭代器位置的改变,因为要返回新插入元素的迭代器。挪动数据这些都小菜一碟。

iterator insert(iterator pos,const T& x)
{assert(pos <= _finish && pos >= _start);if (_finish == _end_of_storage){size_t dx = pos - _start;size_t _capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(_capacity);//迭代器失效,reserve需要开一个新的空间再把原来的空间释放,finish和start都转移了//过去,但是pos还在原空间,变成了野指针pos = _start + dx;}//1 6 4 8 10//1 2 6 4 8 10iterator 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;
}

pop_back和push_back复用insert和erase即可。

resize

void resize(size_t n, const T& val = T())
{if (n < size()){_finish = _start + n;}//_size = 4,_capacity = 7 , 6 8else{reserve(n);for (size_t i = size(); i < n; ++i){_start[i] = val;}_finish = _start + n;}
}

各种构造:

vector(size_t n, const T& val = T())
{resize(n, val);
}
vector(int n, const T& val = T())
{resize(n, val);
}
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}
vector(std::initializer_list<T> il)
{T* tmp = new T[il.size()];for (size_t i = 0; i < il.size(); ++i){tmp[i] = *(il.begin() + i);}_start = tmp;_end_of_storage = _finish = _start + il.size();
}
vector(const vector<T>& ot)
{T* tmp = new T[ot.size()];for (size_t i = 0; i < ot.size(); ++i){tmp[i] = ot[i];}_start = tmp;_finish = _end_of_storage = _start + ot.size();
}
vector(vector<T>&& ot):_finish(nullptr),_start(nullptr),_end_of_storage(nullptr)
{swap(ot);
}

注意:拷贝构造通常只拷贝到size处,并非拷贝整个容量。

这里你可能会疑问,为什么用n个T元素构造vector有两个版本?一个传的size_t,一个int,正常不就是size_t吗,看下面这种情况:
在这里插入图片描述
10和3都是int类型,编译器会默认走最匹配的,这里InputIterator first, InputIterator last是一个类型更加匹配导致了编译错误,所以开一个vector(int n, const T& val = T())来防止这种情况的发生。

赋值运算符重载

operator = 的现代写法(string里面类似)
就是复用拷贝构造之后swap一下

vector<T>& operator=(const vector<T>& ot)
{if (&ot == this){return *this;}vector<T> tmp(ot);swap(tmp);return *this;
}

[]

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

个人gitee

四、浅拷贝的问题

如果你移动元素用的是memcpy,此时可能一些情况下程序就会崩溃。
分析:

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
    所以:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
    使用等于就可以,因为自定义类型会调用operator =,比如string类型就会调用operator=达到深拷贝的效果。

五、优缺点

vector主要对比的对象就是list
优点:
1.vector支持下标访问([] 和 at),O(1)拿到元素,效率高
2.尾插效率很高
3.连续内存布局使 CPU 缓存命中率高,遍历速度快。
4.内存开销较list小,因为list内部需要存指针
缺点:
1.中间插入或者删除很低效,需要挪动数据。
2.迭代器插入和删除涉及到失效的问题
3.每次扩容开销很大

总结

vector用的也不少,需要多加练习。
明天更新list,stack,queue

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

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

相关文章

Functional C++ for Fun Profit

Lambda Conf上有人讲C函数式编程。在Functional Conf 2019上&#xff0c;就有主题为“Lambdas: The Functional Programming Companion of Modern C”的演讲。演讲者介绍了现代C中函数式编程相关内容&#xff0c;讲解了如何使用Lambda表达式编写符合函数式编程原则的C代码&…

Python基础理论与实践:从零到爬虫实战

引言Python如轻舟&#xff0c;载你探寻数据宝藏&#xff01;本文从基础理论&#xff08;变量、循环、函数、模块&#xff09;启航&#xff0c;结合requests和BeautifulSoup实战爬取Quotes to Scrape&#xff0c;适合零基础到进阶者。文章聚焦Python基础&#xff08;变量、循环、…

ThingJS开发从入门到精通:构建三维物联网可视化应用的完整指南

文章目录第一部分&#xff1a;ThingJS基础入门第一章 ThingJS概述与技术架构1.1 ThingJS平台简介1.2 技术架构解析1.3 开发环境配置第二章 基础概念与核心API2.1 核心对象模型2.2 场景创建与管理2.3 对象操作基础第三章 基础开发实战3.1 第一个ThingJS应用3.2 事件系统详解3.3 …

关于list

1、什么是listlist是一个带头结点的双向循环链表模版容器&#xff0c;可以存放任意类型&#xff0c;需要显式定义2、list的使用有了前面学习string和vector的基础&#xff0c;学习和使用list会方便很多&#xff0c;因为大部分的内容依然是高度重合的。与顺序表不同&#xff0c;…

Mysql 查看当前事务锁

在 MySQL 中查看事务锁&#xff08;锁等待、锁持有等&#xff09;&#xff0c;可以使用以下方法&#xff1a; 一、查看当前锁等待情况&#xff08;推荐&#xff09; SELECTr.trx_id AS waiting_trx_id,r.trx_mysql_thread_id AS waiting_thread,r.trx_query AS waiting_query,b…

【Keil5-map文件】

Keil5-map文件■ map文件■ map文件

k8s 基本架构

基于Kubernetes(K8s)的核心设计&#xff0c;以下是其关键基本概念的详细解析。这些概念构成了K8s容器编排系统的基石&#xff0c;用于自动化部署、扩展和管理容器化应用。### 一、K8s核心概念概览 K8s的核心对象围绕容器生命周期管理、资源调度和服务发现展开&#xff0c;主要包…

Bell不等式赋能机器学习:微算法科技MLGO一种基于量子纠缠的监督量子分类器训练算法技术

近年来&#xff0c;量子计算&#xff08;Quantum Computing&#xff09; 和 机器学习&#xff08;Machine Learning&#xff09; 的融合成为人工智能和计算科学领域的重要研究方向。随着经典计算机在某些复杂任务上接近计算极限&#xff0c;研究人员开始探索量子计算的独特优势…

Edge浏览器设置网页自动翻译

一.浏览网页自动翻译设置->扩展->获取Microsoft Edge扩展->搜索“沉浸式翻译”->获取 。提示&#xff1a;如果采用其他的翻译扩展没找自动翻译功能&#xff0c;所以这里选择“沉浸式翻译”二.基于Java WebElement时自动翻译Java关键代码&#xff1a;提示&#xff1…

TCP/UDP协议深度解析(四):TCP的粘包问题以及异常情况处理

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 本系列往期内容~ TCP/UDP协议深度解析&#xff08;一&#xff09;&#xff1a;UDP特性与TCP确认应答以及重传机制 TCP/UDP协议深…

R 基础语法

R 基础语法 R 语言是一种针对统计计算和图形表示而设计的编程语言&#xff0c;广泛应用于数据分析、统计学习、生物信息学等领域。本文将为您介绍 R 语言的基础语法&#xff0c;帮助您快速入门。 1. R 语言环境搭建 在开始学习 R 语言之前&#xff0c;您需要安装并配置 R 语言环…

语义熵怎么增强LLM自信心的

语义熵怎么增强LLM自信心的 一、传统Token熵的问题(先理解“痛点”) 比如模型回答“阿司匹林是否治疗头痛?”→ 输出“是” 传统Token熵:只看“词的概率”,比如“是”这个词的概率特别高(Token熵0.2,数值低说明确定性强 )。 但实际风险:医学场景里,“是”的字面肯定…

javaweb的几大常见漏洞

CTF javaweb中几大常见漏洞(基于java-security靶场) 对于CTF而言&#xff0c;java类型的题目基本都是白盒代码审计&#xff0c;在java类型的web题目增长的今天&#xff0c;java代码审计能力在ctf比赛中尤为重要。 这篇博客主要是给大家介绍一下一些常见漏洞在java代码里面大概是…

【设计模式C#】外观模式(用于解决客户端对系统的许多类进行频繁沟通)

一种结构性设计模式。特点是将复杂的子系统调用逻辑封装到一个外观类&#xff0c;从而使客户端更容易与系统交互。优点&#xff1a;简化了接口的调用&#xff1b;降低了客户端与子系统的耦合度&#xff1b;封装了子系统的逻辑。缺点&#xff1a;引入了额外的类&#xff0c;可能…

【PTA数据结构 | C语言版】二叉堆的快速建堆操作

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个顺序存储的数据用快速建堆操作调整为最小堆&#xff1b;最后顺次输出堆中元素以检验操作的正确性。 输入格式&#xff1a; 输入首先给出一个正整数 c&#xff08;≤1…

【数据结构初阶】--双向链表(二)

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

vue-cli 模式下安装 uni-ui

目录 easycom 自定义easycom配置的示例 npm安装 uni-ui 准备 sass 安装 uni-ui 注意 easycom 传统vue组件&#xff0c;需要安装、引用、注册&#xff0c;三个步骤后才能使用组件。easycom将其精简为一步。 只要组件路径符合规范&#xff08;具体见下&#xff09;&#…

JavaSE-接口

概念在Java中&#xff0c;接口可以被看成是一种公共规范&#xff0c;是一种引用数据类型。语法1.接口的定义格式与类的定义格式基本相同&#xff0c;将class关键字替换为interface关键字&#xff1a;public interface IShape {}2.类与接口之间使用implements关键字来实现接口&a…

常用类学习

文章目录字符串相关的类String的特性String对象的创建字符串相关的类String类与其他结构之间的转换StringBuffer,StringBuilderStringBuffer类的常用方法JDK8之前日期时间APIjava.lang.System类java.util.Date类java.text.SimpleDateFormat类java.util.Calendar类JDK8中新日期时…

【Python库包】Gurobi-Optimize (求解 MIP) 安装

目录Step1&#xff1a;注册账号Step2&#xff1a;获取Licence另&#xff1a;完整安装 Gurobi软件参考本博客简介Gurobi-Optimizer的安装&#xff08;Python 环境&#xff09;。 Step1&#xff1a;注册账号 官网-Gurobi-Optimizer ⚠️注意&#xff1a; Gurobi 是商业软件&…