【C++】vector常用方法总结
📝前言:
在C++中string常用方法总结中我们讲述了string
的常见用法,vector
中许多接口与string
类似,作者水平有限,所以这篇文章我们主要通过读vector官方文档的方式来学习vector
中一些较为常见的重要用法。
🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏
vector常见方法
- 一,vector的定义
- 1. vector的构造函数
- 2. 迭代器
- 3. 容量操作
- 4. 增删查改操作
- 5. 迭代器失效问题
- 5.1. 插入元素引发的迭代器失效
- 5.2. 删除元素导致的迭代器失效
- 5.3. 调整容器大小引起的迭代器失效
- 5.4. 避免迭代器失效的方法
- 1. 插入元素时
- 2. 删除元素时
- 3. 调整容器大小时
- 6. 小知识点补充
一,vector的定义
vector
原型:template < class T, class Alloc = allocator<T> > class vector; // generic template
也就是说
vector
是一个模板,T
可以是任意类型,如int
,string
,甚至是vector<int>
(此时是一个二维数组)
特点:
- 动态大小:
vector
可以在运行时按需调整大小,可以添加或移除元素。 - 随机访问:能在常数时间内通过索引访问任意元素。
- 连续存储:元素在内存中连续存储
- 自动内存管理:vector会自动处理内存分配和释放
1. vector的构造函数
const allocator_type& alloc = allocator_type()
是一个有关内存分配的参数,它的作用是指定 vector
所使用的内存分配器。内存分配器负责管理 vector
内部元素存储所需的内存。(在这里我们先不管它)
vector()
是vector的无参默认构造,会创建一个空的vector
对象vector (size_type n, const value_type& val = value_type())
构造并初始化n
个val
。
这里的value_type()
是调用value_type
的构造函数,用匿名对象来初始化val
。(对于内置类型,为了统一,内置类型也会有默认构造,不过默认构造不做任何处理,如:int a = int()
这里相当于用0来初始化a
)vector (InputIterator first, InputIterator last)
用区间迭代器初始化构造(注意是左闭右开原则)vector (const vector& x)
拷贝构造(深拷贝)
2. 迭代器
vector
也有和string
类似的迭代器,值得注意的是end
和rend
的位置
3. 容量操作
接口 | 接口说明 |
---|---|
size | 获取元素个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
reserve | 改变capacity |
resize | 改变size |
这些接口和方法的特性和string
一样就不过多赘述
4. 增删查改操作
接口 | 接口说明 |
---|---|
push_back | 尾插 |
pop_back | 尾删 |
find | 查找 |
insert | 在position插入val(原来position位置的元素后移) |
erase | 从position位置开始删除数据 |
swap | 交换两个vector的数据空间 |
operator[] | 像数组一样访问 |
注意find
是算法模块实现,不是vector的成员接口。这些接口和方法的特性和string
一样就不过多赘述
5. 迭代器失效问题
在使用vector
的迭代器时,若容器的底层空间改变(如扩容),就可能导致迭代器失效。
迭代器失效就是:迭代器已经不是指向原来的数据,此时意义发生改变(可能指向别的位置,也可能是随机值)
迭代器失效后,我们就不能访问原来已经失效的的迭代器。在vs
中会强制检查,访问即报错,但是在Linux中不强制检查。
5.1. 插入元素引发的迭代器失效
当在vector
里插入元素时,若insert
操作致使容器重新分配内存(扩容),那么所有指向该容器的迭代器都会失效。这是因为重新分配内存时,容器中的元素会被移动到新的内存位置,而原来的迭代器依旧指向旧的内存地址,原来的地址又被释放,此时就指向随机值了(类似野指针)。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin();// 插入元素可能导致内存重新分配vec.insert(vec.begin(), 0); // 此时 it 已经失效,不能再使用// std::cout << *it << std::endl; // 错误:使用失效的迭代器return 0;
}
5.2. 删除元素导致的迭代器失效
从vector
中删除元素时,被删除元素及其后面的所有迭代器都会失效。因为删除元素后,后面的元素会向前移动填补空缺,原有的迭代器指向的位置不再正确。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin() + 1;// 删除元素vec.erase(vec.begin()); // 此时 it 已经失效,不能再使用// std::cout << *it << std::endl; // 错误:使用失效的迭代器return 0;
}
5.3. 调整容器大小引起的迭代器失效
调用vector
的resize
、reserve
等方法改变容器大小时,若触发了内存重新分配,所有迭代器都会失效。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin();// 调整容器大小可能导致内存重新分配vec.resize(10); // 此时 it 已经失效,不能再使用// std::cout << *it << std::endl; // 错误:使用失效的迭代器return 0;
}
5.4. 避免迭代器失效的方法
1. 插入元素时
插入元素后,使用插入操作返回的迭代器来更新原有的迭代器。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin();// 插入元素并更新迭代器it = vec.insert(it, 0); std::cout << *it << std::endl; // 正确:使用更新后的迭代器return 0;
}
2. 删除元素时
删除元素后,使用删除操作返回的迭代器来更新原有的迭代器。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin() + 1;// 删除元素并更新迭代器it = vec.erase(vec.begin()); std::cout << *it << std::endl; // 正确:使用更新后的迭代器return 0;
}
3. 调整容器大小时
在调用resize
、reserve
等方法后,若发生了内存重新分配,要重新获取迭代器。
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3};auto it = vec.begin();vec.resize(10);// 重新获取迭代器it = vec.begin(); std::cout << *it << std::endl; // 正确:使用更新后的迭代器return 0;
}
6. 小知识点补充
1,memmove
和 memcpy
和 strcpy
三者功能差不多,strcpy
针对字符,memcpy
直接复制字节,memmove
也复制字节,但是可以解决内存重叠问题。在无内存重叠的时候memcpy
性能更好
2,当我们在没有实例化的类模板里面取东西,如:vector<T> a
,此时编译器无法判断a
是类型还是变量。这时候,如果是类型,需要在前面加上typename
,变成:typename vector<T> a b
(这样就可以用类型a
定义b
)
3,如果已经写了构造函数,还是想让编译器生成默认构造:vector() = default
4,传参的时候后面跟u
表示unsigned int
,可以避免函数重构,调用时的歧义。如:vector(10u,1)
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!