[C++笔记]vector
vector
vector的说明文档
- vector是表示可变大小数组的序列容器(动态顺序表)。
- 就像数组一样,vector也采用连续的存储空间来储存元素。这就意味着可以用下标对vector的元素进行访问,和数组一样高效。与数组不同的是,它的大小可以动态改变——由容器自动处理。
- 底层而言,vector使用动态分配的数组来存储元素。当新元素插入时,这个数组可能需要被重新分配大小(扩容)。其做法为,分配一个新的数组,然后将所有元素移到这个新数组。就时间成本而言,这是一个代价较高的行为,因此不会在每次向容器添加元素时都重新分配大小。
- vector会分配一些额外的空间以应对可能需要的扩容,因此容器的实际容量大小可能大于容纳其元素所必须的存储空间的大小。不同的库采用不同的策略来权衡空间的使用(usage)与重新分配(reallocation)。但在任何情况下,重新分配都应该只发生在对数增长的大小间隔上,以使得尾插一个元素的时间复杂度为常数。
- 因此,相较于数组,vector占用了更多的存储空间以获得管理存储空间的能力,并以一种高效率的方式动态增长。
- 与其它动态序列容器相比(deque(双端队列),lists(双链表),forward_lists(单链表)), vector在访问其元素时非常高效(就像数组一样),而且从其末端添加或删除元素时也相对高效。对于在末端以外的位置插入或删除元素的操作,它的效率较低。而且与list和forward_lists相比,它的迭代器与引用不太一致。
string是负责管理字符类型的数组,而vector是负责管理任意类型的数组 。
string相比vector有太多冗余的接口,vector的设计相对更合理些。
vector构造函数
vector构造函数 | 接口说明 |
---|---|
vector()(重点) | 无参构造 |
vector (const vector& x); (重点) | 拷贝构造 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
vector容量管理
接口 | 接口说明 |
---|---|
resize(重点) | 改变vector的size |
reserve (重点) | 改变vector的capacity |
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
- capacity的代码在vs和g++下分别运行可以发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。vector每次增容增多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
- reserve只负责开辟空间。如果明确知道需要用多少空间,可使用reserve来缓解vector增容代价较高的问题。
- resize在开空间的同时还会进行初始化,影响size。
vector增删查改
接口 | 接口说明 |
---|---|
push_back | (重点) 尾插 |
pop_back | (重点) 尾删 |
operator[] | (重点) 像数组一样访问 |
find | 查找。(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换两个vector的数据空间 |
vector迭代器
接口 | 接口说明 |
---|---|
begin +end(重点) | 获取第一个元素位置的iterator/const_iterator, 获取最后一个元素的下一位的iterator/const_iterator |
rbegin + rend | 获取最后一个元素位置的reverse_iterator,获取第一个元素前一位的reverse_iterator |
vector 迭代器失效问题(重点)
- 迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际上就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。
- 而迭代器失效,实际上就是迭代器底层所对应的指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果便是程序崩溃(即,若继续使用已经失效的迭代器,程序可能会崩溃)。
===
对于vector,可能会导致其迭代器失效的操作有:
1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
2.指定位置元素的删除操作–erase
3.注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
-SGI STL中,迭代器失效后,代码不一定会崩溃,但运行结果肯定不
对。若it不在begin和end范围内,肯定会崩溃。
4.与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
迭代器失效解决办法:在使用前,对迭代器重新赋值即可。
模拟实现vector
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;namespace my_vector {template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;vector(){}vector(size_t n, const T& val = T()) /*由于要兼容所有类型,缺省值不能为0,而是调默认构造函数构造匿名对象*/{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(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}*///原生方式实现深拷贝vector(const vector<T>& v){_start = new T[v.capacity()];//memcpy(_start, v._start, sizeof(T) * v.size());//memcpy是浅拷贝,不适用于自定义类型for (size_t i = 0; i < v.size(); ++i){_start[i] = v._start[i];}_finish = _start + v.size();_end_of_storage = _start + v.capacity();}template<class InputIterator>//允许类的成员函数本身是函数模板vector(InputIterator first, InputIterator last)/*迭代器区间 [first,last)*/{while (first != last) {//这里不能比较大小,有的结构,如链表,前后节点的地址大小关系并不确定push_back(*first);++first;}}~vector() {delete[] _start;_start = _finish = _end_of_storage = nullptr;}iterator begin() {return _start;}iterator end() {return _finish;}const_iterator begin() const {return _start;}const_iterator end() const {return _finish;}void resize(size_t n, T val = T()) {//由于要兼容所有类型,缺省值不能为0,而是调默认构造函数构造匿名对象 if (n < size()) {_finish = _start + n;//等效于删除数据}else {if (n > capacity()) {reserve(n);while (_finish != _start + n) {*_finish = val;++_finish;}}}}void reserve(size_t n) {if (n > capacity()) {size_t sz = size();//提前记录sizeT* tmp = new T[n];if (_start) {//memcpy(tmp, _start, sizeof(T) * size());//memcpy是浅拷贝,不适用于自定义类型for (size_t i = 0; i < sz; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;//size()返回的是_finish - _start ,这里用size()的话会变成_finish=_start+_finish-_start ,_finish会变成0 ,只能用提前记录的sz_end_of_storage = _start + n;}}size_t capacity() const {return _end_of_storage - _start;}size_t size() const {return _finish - _start;}bool empty() {return _start == _finish;}T& operator[](size_t pos) {assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const {assert(pos < size());return _start[pos];}void push_back(const T& x) {if (_finish == _end_of_storage) {//容量检查reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back() {assert(!empty());//确保非空--_finish;}iterator insert(iterator pos, const T& val) {assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage) {//容量检查size_t len = pos - _start;//为防迭代器失效,需要在扩容前记录pos到_start的相对距离,以便在扩容后更新pos位置。reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//扩容后用记录的相对距离更新pos位置,避免pos位置错误导致的迭代器失效(野指针型)}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);//pos不能等于_finishiterator start = pos + 1;while (start != _finish) {//挨个往前挪*(start - 1) = *start;++start;}--_finish;return pos;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};//===========类分隔线===========void func(const vector<int>& v) {cout << endl;for (size_t i = 0; i < v.size(); ++i) {cout << v[i] << " ";}cout << endl;vector<int>::const_iterator it = v.begin();while (it != v.end()) {cout << *it << " ";++it;}cout << endl;cout << endl;}void test_vector1() {vector<int> v1;v1.push_back(0);v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);func(v1);for (size_t i = 0; i < v1.size(); ++i) {cout << v1[i] << " ";}cout << endl;v1.pop_back();v1.pop_back();vector<int>::iterator it = v1.begin();while (it != v1.end()) {cout << *it << " ";++it;}cout << endl;for (auto e : v1) {cout << e << " ";}cout << endl;}template<class T>void f() {T x = T();cout << x << endl;}void test_vector2() {//虽然内置类型理论上没有构造函数,但为兼容模板,一般支持像自定义类型一样用构造函数//int i = int();//会初始化为0//int j = int(1);//会初始化为1f<int>();f<int*>();f<double>();}void test_vector3() {vector<int> v1;v1.push_back(0);v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);cout << v1.size() << endl;cout << v1.capacity() << endl;v1.resize(10);cout << v1.size() << endl;cout << v1.capacity() << endl;func(v1);v1.resize(3);func(v1);}void test_vector4() {vector<int> v1;v1.push_back(0);v1.push_back(1);v1.push_back(2);v1.push_back(3);//v1.push_back(4);//*v1.insert(v1.begin(), 9);auto pos = find(v1.begin(), v1.end(), 2);if (pos != v1.end()) {//pos = v1.insert(pos, 50);v1.insert(pos, 50);}//insert后的pos默认看作已失效(野指针),不能再使用,因为位置关系变了for (auto e : v1) {cout << e << " ";}cout << endl;}void test_vector5() {vector<int> v1;v1.push_back(0);v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1) {cout << e << " ";}cout << endl;auto pos = find(v1.begin(), v1.end(), 3);if (pos != v1.end()) {v1.erase(pos);}//类似于insert后,erase后pos也视作失效(野指针),因为位置关系也变了//(比如pos指向最后一个元素,erase这个元素后pos就指向了_finish,不再指向合法位置)//(*pos)++;//vs下会被检查到然后报错,linux下g++不检查但还是可能会出问题(行为结果未定义,与具体编译器实现有关)//string也会有迭代器失效的情况,但string的erase和insert更常用下标而不是迭代器,故问题不算明显。for (auto e : v1) {cout << e << " ";}cout << endl;}void test_vector6() {vector<int> v1;v1.push_back(0);v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1) {cout << e << " ";}cout << endl;//删除所有偶数:迭代器适合删除vector<int>::iterator it = v1.begin();while (it!=v1.end()){if (*it % 2 == 0) {it = v1.erase(it);//迭代器erase后失效,接收返回值以更新pos}else {++it;//迭代器未失效,正常++}}for (auto e : v1) {cout << e << " ";}cout << endl;}void test_vector7() {//vector<int> v1(10u, 5);//u表示无符号vector<int> v1(10, 5);for (auto e : v1) {cout << e << " ";}cout << endl;vector<int> v2(v1.begin() + 1, v1.end() - 1);for (auto e : v2){cout << e << " ";}cout << endl;std::string s1("hell word");vector<int> v3(s1.begin(), s1.end());for (auto e : v3){cout << e << " ";}cout << endl;int arr[] = {1,2,60,3,4,5};vector<int> v4(arr, arr + 4);for (auto e : v4){cout << e << " ";}cout << endl;v1.insert(v1.begin(), 8);for (auto e : v1){cout << e << " ";}cout << endl;sort(v1.begin(), v1.end());for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : arr){cout << e << " ";}cout << endl;sort(arr, arr + sizeof(arr) / sizeof(int), greater<int>());//sort默认升序。除了能对迭代器的区间排序,还可以对数组排序。for (auto e : arr){cout << e << " ";}cout << endl;}void test_vector8(){vector<int> v1(10, 5);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2(v1);for (auto e : v2){cout << e << " ";}cout << endl;vector<std::string> v3(3, "123456789123456789");for (auto e : v3){cout << e << " ";}cout << endl;vector<std::string> v4(v3);for (auto e : v4){cout << e << " ";}cout << endl;v4.push_back("1111111111");v4.push_back("2222222222");v4.push_back("3333333333");for (auto e : v4){cout << e << " ";}cout << endl;}}
相关文章:

[C++笔记]vector
vector vector的说明文档 vector是表示可变大小数组的序列容器(动态顺序表)。就像数组一样,vector也采用连续的存储空间来储存元素。这就意味着可以用下标对vector的元素进行访问,和数组一样高效。与数组不同的是,它的大小可以动态改变——…...

Python 迁移学习实用指南:1~5
原文:Hands-On Transfer Learning with Python 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只关心如…...

【CSS重点知识】属性计算的过程
✍️ 作者简介: 前端新手学习中。 💂 作者主页: 作者主页查看更多前端教学 🎓 专栏分享:css重难点教学 Node.js教学 从头开始学习 ajax学习 标题什么是计算机属性确定声明值层叠冲突继承使用默认值总结什么是计算机属性 CSS属性值的计算…...

Java避免死锁的几个常见方法(有测试代码和分析过程)
目录 Java避免死锁的几个常见方法 死锁产生的条件 上死锁代码 然后 :jstack 14320 >> jstack.text Java避免死锁的几个常见方法 Java避免死锁的几个常见方法 避免一个线程同时获取多个锁。避免一个线程在锁内同时占用多个资源,尽量保证每个锁…...

go binary包
binary包使用与详解 最近在看一个第三方包的库源码,bigcache,发现其中用到了binary 里面的函数,所以准备研究一下。 可以看到binary 包位于encoding/binary,也就是表示这个包的作用是编辑码作用的,看到文档给出的解释…...

CompletableFuture使用详解(IT枫斗者)
CompletableFuture使用详解 简介 概述 CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力…...

4.15--设计模式之创建型之责任链模式(总复习版本)---脚踏实地,一步一个脚印
一、什么是责任链模式: 责任链模式属于行为型模式,是为请求创建了一个接收者对象的链,将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。 当一个请求从链式的首端发出时&a…...

STM32+W5500实现以太网通信
STM32系列32位微控制器基于Arm Cortex-M处理器,旨在为MCU用户提供新的开发自由度。它包括一系列产品,集高性能、实时功能、数字信号处理、低功耗/低电压操作、连接性等特性于一身,同时还保持了集成度高和易于开发的特点。本例采用STM32作为MC…...

全网最详细,Jmeter性能测试-性能基础详解,终成测试卷王(一)
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 发起请求 发起HTTP…...

人工智能概述
一、人工智能发展必备三要素 算法 数据 算力 CPU、GPU、TPU 计算力之CPU、GPU对比: CPU主要适合I\O密集型任务GPU主要适合计算密集型任务 什么样的程序适合在GPU上运行? 计算密集型的程序 所谓计算密集型(Compute-intensive)的程序,就是…...

API接口安全—webservice、Swagger、WEBpack
API接口安全—webservice、Swagger、WEBpack1. API接口介绍1.1. 常用的API接口类1.1.1. API接口分类1.1.1.1. 类库型API1.1.1.2. 操作系统型API1.1.1.3. 远程应用型API1.1.1.4. WEB应用型API1.1.1.5. 总结1.1.2. API接口类型1.1.2.1. HTTP类接口1.1.2.2. RPC类接口1.1.2.3. web…...

从前M个字母中取N个的无重复排列 [2*+]
目录 从前M个字母中取N个的无重复排列 [2*+] 程序设计 程序分析 从前M个字母中取N个的无重复排列 [2*+] 输出从前M个字母中取N个的无重复字母排列 Input 输入M N 1<=M=10, N<=M Output 按字典序输出排列 Sample Input 4 2 Sample Output A B A C A D B A B C B …...

ES forceMerge 强制段合并为什么会提升检索性能?
根据以前的测试,forceMerge段合并,将段的个数合并成一个。带来了将近一倍的性能提升,测试过程文档(请参考我的另外一篇文章):ES优化实战- forceMerge搜索提升测试报告_es forcemerge_水的精神的博客-CSDN博…...

macOS Ventura 13.3.1 (22E261) Boot ISO 原版可引导镜像
本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 macOS Ventura 13.3.1 为 Mac 提供下…...

html+css+JavaScript+json+servlet的社区系统(手把手教学)
目录 课前导读: 一、系统前期准备 二、前端代码的编写 三、登陆页面简介 四、注册页面 五、社区列表页 六、社区详情页 七、社区发帖页 八、注销 九、访问链接 登陆页面http://175.178.20.77:8080/java106_blog_system/login.html 总结: 课前…...

UI Toolkit(1)
UI ToolkitUI Toolkit界面画布设置背景制作UI布局UI Toolkit界面 在Unity 2021LTS版本之后UI Toolkit也被内置在Unity中,Unity有意的想让UI Toolkit 成为UI的主要搭建方式,当然与UGUI相比还是有一定的差别。他们各有有点,这次我们就开始介绍…...

vLive带你走进虚拟直播世界
虚拟直播是什么? 虚拟直播是基于5G实时渲染技术,在绿幕环境下拍摄画面,通过实时抠像、渲染与合成,再推流到直播平台的一种直播技术。尽管这种技术早已被影视工业所采用,但在全民化进程中却是困难重重,面临…...

初谈 ChatGPT
引子 最近,小编发现互联网中的大 V 突然都在用 ChatGPT 做宣传:“ChatGPT不会淘汰你,能驾驭ChatGPT的人会淘汰你”、“带领一小部分人先驾驭ChatGPT”。 确实,ChatGPT这个新生事物,如今被视为蒸汽机、电脑、iPhone 般的…...

JAVA练习103-螺旋矩阵
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 提示:这里可以添加本文要记录的大概内容: 4月9日练习内容 提示:以下是本篇文章正文内容,下面案例可供参考 一、题目-螺…...

RecvByteBufAllocator内存分配计算
虽然了解了整个内存池管理的细节,包括它的内存分配的具体逻辑,但是每次从NioSocketChannel中读取数据时,应该分配多少内存去读呢? 例如,客户端发送的数据为1KB , 应该分配多少内存去读呢? 例如:…...

图数据结构与算法
什么是图数据的结构 图是由顶点和边组成的非线性数据结构。顶点有时也称为节点,边是连接图中任意两个节点的线或弧。更正式地说,图由一组顶点 ( V ) 和一组边 ( E ) 组成。该图由 G(E, V) 表示。 图的组成部分 顶点:顶点是图的基本单位。有时,顶点也称为顶点或节点。每个节…...

科普:c语言与C++的区别
C语言和C语言是两种广泛使用的编程语言,尽管它们非常相似,但它们在某些方面也存在不同之处。本文将详细介绍C语言和C语言的区别。 1. 编程范式 C语言是一种过程式编程语言,它的设计目标是为了编写操作系统和其他系统级编程。C语言是一种面向…...

流量整形(GTS和LR)
Generic Traffic Shaping通用流量整形 通用流量整形(简称GTS)可以对不规则或不符合预定流量特性的流量进行整形,以保证网络上下游之间的带宽匹配,避免拥塞发生。 GTS与CAR一样,都采用了令牌桶技术来控制流量。GTS与CAR的主要区别在于:利用CAR进行报文流量控制时,…...

Java接口详细讲解
目录 Java接口概念 Java接口主要有以下特点 Java接口的具体作用 定义接口 实现接口 接口继承 默认方法 静态方法 Java接口概念 Java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。 …...

元宇宙地产暴跌,林俊杰亏麻了
文/章鱼哥出品/陀螺财经随着元宇宙的兴起,元宇宙地产曾一度被寄予厚望,成为各大投资者追捧的对象。然而,最近的一次元宇宙地产价值暴跌再次提醒我们,高收益背后可能伴随着高风险。根据元宇宙分析平台WeMeta的数据显示,…...

什么是瀑布流布局?瀑布流式布局的优缺点
瀑布流又称瀑布流式布局,是一种多列等宽不等高的一种页面布局方式。 视觉表现为参差不齐的多栏布局。随着页面滚动条向下滚动,这种布局会不断加载数据并附加至当前的尾部。 是一种多列等宽不等高的一种页面布局方式,用于图片比较复杂&#…...

给您的 MongoDB 定期做个体检:MongoDB 诊断
新钛云服已累计为您分享739篇技术干货接下来的一些列文章会为大家介绍日常工作中常用的 NoSQL 产品 MongoDB。主要涉及到:MongoDB 的安装及基本使用 MongoDB 文档查询 MongoDB 复制集 MongoDB 分片集群的介绍及搭建 MongoDB 安全加密 MongoDB 诊断我们会用…...

【云原生进阶之容器】第五章容器运行时5.8--容器热迁移
《云原生进阶之容器》专题索引: 第一章Docker核心技术1.1节——Docker综述第一章Docker核心技术1.2节——Linux容器LXC第一章Docker核心技术1.3节——命名空间Namespace第一章Docker核心技术1.4节——chroot技术第一章Docker核心技术1.5.1节——cgroup综述...

react框架的简单认识
React框架 众所周知,React与Vue,Angular被前端开发人员称为前端的三大框架。在如今,React和Vue相对于老牌的Angular,它们的表现更为出色,常常被各大公司使用。但其中React的技术难度要稍稍大于Vue,不过为了…...

IDEA的基本使用
IDEA的基本使用IDEA的基本使用1 IDEA概述2 IDEA的下载和安装2.1 下载2.2 安装3 IDEA中层级结构介绍3.1 结构分类3.2 结构介绍project(项目、工程)module(模块)package(包)class(类)3…...