【C++】vector的基本使用
文章目录
- vector的介绍
- vector的使用
- vector的构造方式
- vector的空间增长问题
- size和capacity
- reserve和resize
- empty
- vector的迭代器使用
- begin和end
- rbegin和rend
- vector的增删查改
- push_back和pop_back
- insert和erase
- **find函数:**
- swap
- 遍历
- operator[]
- 迭代器
- 通用打印任意类型的容器模板 -
- 范围for
- vector迭代器失效问题
- 迭代器失效问题举例
- 迭代器失效解决方法
- 迭代器失效例子分析
- 结论:
vector的介绍
1、vector是表示可变大小数组的序列容器,
2、vector就像数组一样,也采用的连续空间来存储元素,这也意味着可以采用下标对vector的元素进行访问,
3、vector与普通数组不同的是,vector的大小是可以动态改变的,
4、当vector需要重新分配大小时,其做法是,分配一个新的数组,然后将全部元素移到这个数组当中,并释放原来的数组空间,
5、vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大,不同的库采用不同的策略权衡空间的使用和重新分配,以至于在末尾插入一个元素的时候是在常数的时间复杂度完成的,
6、由于vector采用连续的空间来存储元素,与其他动态序列容器相比,vector在访问元素的时候更加高效,在其末尾添加和删除元素相对高效,而对于不在其末尾进行的删除和插入操作效率则相对较低,
vector的使用
vector的构造方式
方式一: 构造一个某类型的空容器,
vector<int> v1; //构造int类型的空容器
方式二: 构造一个含有n个val的某类型容器,
vector<int> v2(10, 2); //构造含有10个2的int类型容器
方式三: 拷贝构造某类型容器的复制品,
vector<int> v3(v2); //拷贝构造int类型的v2容器的复制品
方式四: 使用迭代器拷贝构造某一段内容,
vector<int> v4(v2.begin(), v2.end()); //使用迭代器拷贝构造v2容器的某一段内容
注意:该方式也可用于拷贝其他容器的某一段内容,
string s("hello world");
vector<char> v5(s.begin(), s.end()); //拷贝构造string对象的某一段内容
注意: s后面是包含\0的 而v5是没有\0的
vector的空间增长问题
size和capacity
通过size函数获取当前容器中的有效元素个数,通过capacity函数获取当前容器的最大容量,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 2);cout << v.size() << endl; //获取当前容器中的有效元素个数cout << v.capacity() << endl; //获取当前容器的最大容量return 0;
}
reserve和resize
通过reserse函数改变容器的最大容量,resize函数改变容器中的有效元素个数&&容量,
reserve只改变容量,不改变size resize改变容量也改变size
reserve规则:
1、当所给值大于容器当前的capacity时,将capacity扩大到该值,
2、当所给值小于容器当前的capacity时,什么也不做,
resize规则:
1、当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,则默认为0,
2、当所给值小于容器当前的size时,将size缩小到该值,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 2);cout << v.size() << endl; //10cout << v.capacity() << endl; //10v.reserve(20); //改变容器的capacity为20,size不变cout << v.size() << endl; //10cout << v.capacity() << endl; //20v.resize(15); //改变容器的size为15cout << v.size() << endl; //15cout << v.capacity() << endl; //20return 0;
}
empty
通过empty函数判断当前容器是否为空,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 2);cout << v.empty() << endl;//判断容器是否为空return 0;
}
vector的迭代器使用
迭代器区间:左闭右开
begin和end
通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的下一个位置的正向迭代器,
正向迭代器遍历容器:
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 2);//正向迭代器遍历容器vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
rbegin和rend
通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器,
反向迭代器遍历容器:
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 2);//反向迭代器遍历容器vector<int>::reverse_iterator rit = v.rbegin();//也可以自动推导类型//auto rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";rit++;}cout << endl;return 0;
}
vector的增删查改
push_back和pop_back
通过push_back函数对容器进行尾插,pop_back函数对容器进行尾删,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v;v.push_back(1); //尾插元素1v.push_back(2); //尾插元素2v.push_back(3); //尾插元素3v.push_back(4); //尾插元素4v.pop_back(); //尾删元素v.pop_back(); //尾删元素v.pop_back(); //尾删元素v.pop_back(); //尾删元素return 0;
}
insert和erase
通过insert函数可以在所给迭代器位置插入一个或多个元素,通过erase函数可以删除所给迭代器位置的元素,或删除所给迭代器区间内的所有元素(左闭右开),
可以插入/删除一个值 或者删除多个值(迭代器区间)
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.insert(v.begin(), 0); //在容器开头插入0 头插v.insert(v.begin(), 5, -1); //在容器开头插入5个-1v.erase(v.begin()); //删除容器中的第一个元素v.erase(v.begin(), v.begin() + 5); //删除在该迭代器区间内的元素(左闭右开)return 0;
}
建议:少用insert和erase 因为它们两个如果是头部,中间插入/删除,效率是O(N),因为它们需要挪动数据
删除指定区间的值:v.erase(v.begin(), v.begin() + val) 但是vector和string才可以这样,因为它们底层是数组,空间连续.但是list和map是不支持这样的!
以上是按位置进行插入或删除元素的方式,若要按值进行插入或删除(在某一特定值位置进行插入或删除),则需要用到find函数,
find函数:
find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值,
find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数,
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器v.insert(pos, 10); //在2的位置插入10pos = find(v.begin(), v.end(), 3); //获取值为3的元素的迭代器v.erase(pos); //删除3return 0;
}
注意: find函数是在算法模块(algorithm)当中实现的,不是vector的成员函数,
swap
通过swap函数可以交换两个容器的数据空间,实现两个容器的交换,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v1(10, 1);vector<int> v2(10, 2);v1.swap(v2); //交换v1,v2的数据 地址不改变return 0;
}
遍历
operator[]
vector当中实现了 [ ] 操作符的重载,因此我们也可以通过“下标+[ ]”的方式对容器当中的元素进行遍历,
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 1);//使用“下标+[]”的方式遍历容器for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;return 0;
}
迭代器
#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v(10, 1);//迭代器vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
普通对象和const对象的遍历, 可以搞成模板,就可以打印任意类型的vector,普通对象和const对象都能调用
通用打印任意类型的容器模板 -
template<class T>
void PrintVector(const vector<T>& v)
{vector<T>::const_iterator it = v.begin();//简写:由auto自己推导//auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;
}
范围for
我们上面说到vector是支持迭代器的,所以我们还可以用范围for对vector容器进行遍历,(支持迭代器就支持范围for,因为在编译时编译器会自动将范围for替换为迭代器的形式)
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v(10, 1);//范围for//自动判断结束,自动迭代++ ->看起来很智能,实际是被替换成了迭代器for (auto e : v){cout << e << " ";}cout << endl;return 0;
}
vector迭代器失效问题
迭代器的主要作用就是让我们在使用各个容器时不用关心其底层的数据结构,而vector的迭代器在底层实际上就是一个指针,迭代器失效就是指迭代器底层对应指针所指向的空间被销毁了,而指向的是一块已经被释放的空间,如果继续使用已经失效的迭代器,程序可能会崩溃,
迭代器失效问题举例
实例一:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//v: 1 2 3 4 5vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器v.insert(pos, 10); //在值为2的元素的位置前插入10//v: 1 10 2 3 4 5v.erase(pos); //删除元素2 ->error(迭代器失效),实际是把10删除掉了//v: 1 2 3 4 5return 0;
}
在该代码中,我们本意是使用元素2的迭代器在原序列中2的位置插入一个10,然后将2删除,但我们实际上获取的是指向2的指针,当我们在2的位置插入10后,该指针就指向了10,所以我们之后删除的实际上是10,而不是2
实例二:删除容器当中的全部偶数
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v;for (size_t i = 1; i <= 6; i++){v.push_back(i);}vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0) //删除容器当中的全部偶数{v.erase(it);}it++;}return 0;
}
该代码看上去实际上并没有什么错误,但如果你画图仔细分析,你就会发现该代码的问题所在,迭代器访问到了不属于容器的内存空间,导致程序崩溃
不仅如此,而且在迭代器遍历容器中的元素进行判断时,并没有对1、3、5元素进行判断,直接跳过了这些元素
注意:end()会更新,返回的是最后一个数据的下一个位置!
如果最后一个数是奇数,正常结束! it == end() 但是最终结果不一定是对的
如果最后一个数是偶数,导致it和end()错开,越界访问,报错!
但是最后一个数是奇数时,linux不报错,vs下无论最后一个数是奇数还是偶数,都会报错,因为erase(it),it失效之后,it++会进行检查
迭代器失效解决方法
使用迭代器时,每次使用前,对迭代器进行重新赋值,
实例一解决方案:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//v: 1 2 3 4 5vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器v.insert(pos, 10); //在值为2的元素的位置插入10//v: 1 10 2 3 4 5pos = find(v.begin(), v.end(), 2); //重新获取值为2的元素的迭代器v.erase(pos); //删除元素2//v: 1 10 3 4 5return 0;
}
对于实例一,我们在使用迭代器删除元素2时对其进行重新查找值为2的元素的迭代器便可以解决
实例二解决方案:
#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> v;for (size_t i = 1; i <= 6; i++){v.push_back(i);}vector<int>::iterator it = v.begin();while (it != v.end()){//删除容器当中的全部偶数if (*it % 2 == 0) {it = v.erase(it); //删除后获取下一个元素的迭代器}else{it++; //是奇数则it++}}return 0;
}
对于实例二,我们可以接收erase函数的返回值(erase函数返回删除元素的后一个元素的新位置),并且控制代码的逻辑:当元素被删除后继续判断该位置的元素(因为该位置的元素已经更新,需要再次判断)
迭代器失效例子分析
迭代器失效:
- 1.意义变了
- 2.野指针 (插入数据时,容量满了增容,开辟新空间,pos仍指向旧空间,但是旧空间已经释放了,旧空间释放了,但是指针还在, 该指针是野指针)
形参position虽然指向新空间的正确位置,但是不影响实参pos. 此时实参pos就是指向非法空间,如何解决呢? 可以传引用!但是不建议,这会引发其它问题
结论:
vector的insert和erase都会导致迭代器失效,用了迭代器改变了底层的结构
1.insert(it,x) 或者 erase(it) 以后迭代器的意义变了
2.insert(it,x) 或者 erase(it),it变成了野指针 (发生了增容/缩容 )
只要使用迭代器访问的容器,都可能设计迭代器失效的问题,string类同样也会失效,但是一般很少设计迭代器失效,因为string的插入和删除一般不使用迭代器进行操作,常用的是下标进行操作
删除vector的所有元素
//写法1:
int main()
{vector<int> v{ 1,2,3,4 };auto it = v.begin();while (it != v.end()){it = v.erase(it);//erase返回删除位置的下一个位置的迭代器}for (auto e : v){cout << e << " ";}
}//写法2:
int main()
{vector<int> v{ 1,2,3,4 };while (!v.empty()){auto it = v.begin();//因为容器不为空,所以可以取begin迭代器v.erase(it);//删除该位置的元素}for (auto e : v){cout << e << " ";}
}
【C++】vector的基本使用相关推荐
- OpenCV 笔记(08)— 二维点、三维点、基于 Mat 的 std::vector 等常用数据结构的定义和输出
1. 定义和输出二维点 Point2f p2(3, 4);cout << "[二维点] is "<< endl << p2 << e ...
- c++中的vector的常见使用
#include <iostream> #include <vector> #include <string> using namespace std; int m ...
- 向量算子优化Vector Operation Optimization
向量算子优化Vector Operation Optimization 查看MATLAB命令View MATLAB Command 示例显示Simulink®编码器™ ,将生成向量的块输出,设置为标量 ...
- java vector search_java.util.Vector.retainAll()方法实例
全屏 retainAll(Collection> c)方法用于仅保留此向量包含在指定Collection的元素.换言之,删除这个向量的所有元素未包含在指定Collection. 声明 以下是ja ...
- C++ 向量(vector) 的使用
向量(vector)是什么 向量(vector)是属于STL(Standard Template Library, 标准模板库)中的一种随机访问数组的类型. 使用的时候需要使用#include < ...
- C++ 笔记(23)— STL vector 类(实例化 vector、末尾插入、指定位置插入、数组方式访问元素、指针方式访问元素、删除元素、大小与容量区别)
1. vector 特点 vector 是一个模板类,提供了动态数组的通用功能,具有如下特点: 在数组末尾添加元素所需的时间是固定的,即在末尾插入元素的所需时间不随数组大小而异,在末尾删除元素也如此: ...
- 比较ArrayList、LinkedList、Vector
翻译人员: 铁锚 翻译时间: 2013年12月2日 原文链接: ArrayList vs. LinkedList vs. Vector 1. List概述 List,就如图名字所示一样,是元素的有序列 ...
- C++ stl vector介绍
转自: STL vector用法介绍 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if ...
- 【stanford C++】容器III——Vector类
主要介绍如下5个容器类--Vector, Stack,Queue,Map和Set,各个都表示一重要的抽象数据类型.另外,各个类都是一些简单类型的值的集合,所以称它们为容器类. 暂且我们先不需要知道它们 ...
- STL vector list deque区别与实现
1 vector 向量 相当于一个数组 在内存中分配一块连续的内存空间进行存储.支持不指定vector大小的存储.STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacitu ...
最新文章
- git reset --hard xxxxxxx
- oracle中把函数的执行权限赋个某个用户
- 左神算法:最大值减去最小值小于或等于num的子数组的数量(Java版)
- java调用php session_php读取memcahed java session
- 【原创】uC/OS 中LES BX,DWORD PTR DS:_OSTCBCur的作用及原理
- 09.07 jQuery 随意整理
- 图书管理系统活动,时序图
- php 当前时间转换,php时间转换
- 【突发】解决remote: Support for password authentication was removed on August 13, 2021. Please use a perso
- 3814.矩阵变换-AcWing题库
- 将PDF转为TXT文本格式提取中文
- 《JAVA语言程序设计与数据结构》(基础篇)原书第11版 第一章(答案)
- 算法比赛玩腻了?试试这个不一样的 Java 编程挑战赛!
- 3.3Packet Tracer - 实施基本连接
- SVN入门教程,超简单,30分钟学会!
- Android dex2oat命令参数解释
- [Other]B树 B+树 B*树 - 三大名树的基础简介
- 学计算机进中央电视台,厉害了,我们的计算机!
- 焦虑症应该怎么办?这六个缓解方法建议试试
- Ubuntu18.04配置YCM
热门文章
- 数据链路层——MAC地址欺骗及泛洪
- [高通SDM450][Android9.0]CTA认证--去除某些应用开机使用定位权限
- 计算机小学期实践报告,小学期计算机实践报告
- 不需要再手写 onSaveInstanceState 了,因为你的时间非常值钱
- 计算机基础知识第三章答案,2011年河北省职称计算机模拟习题(基础知识第三章+标准答案)...
- linux下shell脚本启动其他可执行程序
- linux上的两种可执行程序
- 疯狂java笔记(七) - Java集合之Map
- 【教程】如何使用ArcGIS绘制荧光图
- 项目启动大会的注意事项