C++ vector 核心用法:insert/erase、reserve/resize、嵌套类型与迭代器失效
vector
它是我们学过的顺序表
1. insert 和 erase 用迭代器定位
vector中的insert在使用时的位置参数使用的是迭代器不是下标
c++
#include<vector>
#include<iostream>
using namespace std;
vector<int> v1(10,1);
//头插"0"
v1.insert(v1.begin(), 0)
//中间下标为5的位置插入"0"
v1.insert(v1.begin()+5, 0);
//尾插"0"
v1.insert(v1.end(), 0);
//越界均会报错
erase也是同理,使用的也是迭代器
begin指向的是下标为0的位置,而end指向的是最后一个位置的下一个位置,在使用erase删除一段区间时,比如删除下标0~4的值,那么应该写为erase(v1.begin(), v1.begin()+5)要牢记STL中的类似函数作用区间均是左闭右开区间。
2. reserve 与 resize
reserve:
在对类进行初始化时,不能用reserve,它只是用来提效的,作用只是提前开好一块空间预留,并没有其他作用,只是预留一块空间实际上还并没有元素,可能创建capacity,但还没有创建元素,这时候访问 v[0] 也是未定义行为,必须通过push_back来添加元素因为元素根本还没构造出来,主要用于提前知道元素数量时,减少扩容和内存搬迁
同时,在使用reserve时,capacity可能增大,并保证一定增大,
c++
当前 capacity < n → 容量增大
当前 capacity >= n → 容量不变
另外,新容量可能大于n,例如
v.reserve(10);
最终容量可能是10、16、20等,具体有标准库决定,只保证不少于10,resize也是这样的
resize:
改变元素数量,必要时创建或销毁元素
当需要使用下标访问写入元素时,推荐使用resize,因为resize后元素真实存在
3. vector 套类型
vector还可以套类型,比如:
c++
#include<vector>
#include<iostream>
vector<string> v;
再来介绍一种情况:
c++
void test_vector2()
{
vector<string> v2;
v2.push_back("张三");
v2.push_back("李四");
v2.push_back("abcdefghijk");
//for (auto e : v2)
//{
// cout << e << " ";
//}
//cout << endl;
//因为这里的类型是string,在auto e时会触发string的拷贝构造进行深拷贝,开销比较大,因此更好的写法是使用引用
for (const auto& e : v2)//这里只读,加上const更为标准
{
cout << e << " ";
}
cout << endl;
}

这里的v220,第一个[2]是vector中的第3个string,第二个[0]则指向了第三个string中串的第1个元素,并将那个元素修改为y,即将a修改为y
c++
v2[2][0] = 'y';
//等价于
v2.operator[](2).operator[](0) = 'y';
还可以写成这个:
c++
#include<vector>
vector<vector<int>> v;
类似套娃

4. 迭代器失效
迭代器的主要作用就是让算法不用关心底层数据结构,其实底层实际就是一个指针,或是对指针进行了封装,比如vector的原生指针就是T*。因此迭代器失效实际上就是迭代器底层指针指向的空间被销毁了,但迭代器还在使用被销毁的这一块空间,造成的后果就是程序崩溃
对使用vector会导致其迭代器失效的操作有:
-
引起底层空间改变的操作都有可能使迭代器失效,如:resize、reserve、insert、assign、push_back等,下面图片以insert为例:

自己写的erase在迭代器使用后也认定失效,去除了在不同平台运行程序存在的差异性
在VS中给了个强制检查,当我们使用erase删除一个元素后,后面的值补位上来,
虽然迭代器的位置没变,但是它指向的 值已经被补位的值给补位掉了,在vs中设置了对迭代器的强制检查,标准规定这样的迭代器已经失效,VS对失效的迭代器访问进行了严格的检查,而在g++下程序可以跑但也会存在问题:
写一个简单的程序:删除偶数。对于连续的偶数或最后一个数是偶数在g++中会被吞
c++
for(auto it=v.begin();it!=v.end();++it)
{
if(e%2==0)
v.erase(e);//erase后当前迭代器失效,以及it以后的迭代器均失效
}
//正确写法
for (auto it = v.begin(); it != v.end(); )
{
if (*it % 2 == 0)
it = v.erase(it); // erase 返回下一个有效位置并接收
else
++it;
}
5. 默认构造与报错
vector() = default强制编译器生成默认构造(当缺省值给到位了并且有拷贝构造函数时,我们写构造函数只是为了让编译器不会因为拷贝构造函数存在编译器不自动生成默认构造函数且我们又因为没有直接需求而不写拷贝构造导致最后没有构造函数的出现)
错误说明:
"非法间接寻址"==指针解引用错误

完