vector原理、存储、遍历、缩容、迭代器失效问题
vector比较详细的原理及用法
存储数据
void text()
{
// 存储int类型数据,当然也可以存储其它类型如char、string、类等
vector<int> vec;
vec[0] = 10;
vec[1] = 9;
}
-
上面这种存储方式是错误的!
vector
是数组的确没有错,但是最开始的vec
是没有大小的,所以是没有办法用下标这种方式赋值的。 -
可以把
vec
的容量(capacity
:数组的大小)和大小(size
:数组中元素的个数)打印出来cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
结果是size = 0, capacity = 0
-
正确的存储方式如下:
vector<int> vec; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(1); vec.push_back(2); vec.push_back(3); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
运行结果:
size = 0, capacity = 0 size = 3, capacity = 4
-
可以发现通过调用
vec
的成员函数push_back()
函数存储3个数据后,vec
的大小是3,capacity
是4。有同学可能当时就蒙了,4哪来的啊?别急,后面会讲解vector
的扩容机制的!我们先从最简单的一点一点看。有同学又会说了,我哪知道vec有啥成员函数啊?其实可以通过cppreference
网站查看c++
的一些函数相关信息,内容很全。传送门:https://en.cppreference.com/w/;英文看不懂没关系,还有中文版https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5 -
其实还是有办法可以用下标的方式给vector数组赋值的
void test() { vector<int> vec; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; // 设置vec的大小是3,(size=capacity=3) vec.resize(3); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; // vec有了大小之后,就可以采用下标的方式赋值了 vec[0] = 1; vec[1] = 2; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; }
-
还可以在创建vec对象时直接初始化
// 初始化vec的数据 vector<int> vec = {1, 2, 3}; // 还可以指定vec的大小为5,默认初始值为0 vector<int> vec1(5); // 指定vec大小同时并初始化元素为2 vector<int> vec2(5, 2); //打印数组元素,下面马上讲解打印数组元素 for (auto elem : vec2){ cout << elem << endl; }
遍历vector数组
遍历方式一(上面刚见过):
vector<int> vec;
cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
for (auto elem : vec){
cout << elem << endl;
}
-
for
循环中的auto
关键字是c++
中自动类型推导,当然你将auto
换成int
一点毛病没有。 -
还以以对遍历的值进行修改,如将元素值每个都加一
for (int &elem : vec){ cout << elem << endl; elem += 1; }
-
注意哦,我这里的
for
循环可是带了引用&的,所以可以修改值哦。ps:引用就是变量的别名
遍历方式二:常规方式
for (size_t i = 0; i < vec.size(); ++i){
cout << vec[i] << endl;
}
- 注意:这里的判断条件是
i < vec.size()
而不是i < vec.capacity()
;上面说过了,size
代表的是数组元素的个数,capacity
代表的是数组的容量大小。就好比你买了一个书架,书架可以放5本书,但你只放了2本书,那么这个5就是书架的容量,2代表当前书架拥有书的数量 - 有同学
for
循环写成for (auto i = 0; i < vec.size(); ++i)
,会报warning
。然后就疑惑了,说auto不是能自动推导类型么?咋就报类型不匹配了呢?因为你把i
初始化为0,那么自动推导出来的i
是int
类型了。而vec.size()
返回的是size_t
类型(其实就是一个unsigned long
),(对于返回类型可以在cppreference
看到的很清楚)
遍历方式三:迭代器方式
for (auto it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
vector的存储机制
-
先看代码
vector<int> vec; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(1); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(2); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(3); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(4); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.push_back(5); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
运行结果:
size = 0, capacity = 0 size = 1, capacity = 1 size = 2, capacity = 2 size = 3, capacity = 4 size = 4, capacity = 4 size = 5, capacity = 8
-
基本上这些数据就可发现,
size
与插入的总元素个数是相同的,插入一个元素,那么size就+1;而capacity的增长是按照之前有数据容量的2倍增长的(windows貌似是1.5倍增长的)。 -
vector的容量机制是:当数组中的size==capacity之后,若是还需要存储数据,系统会重新申请一块内存空间,空间大小是之前的2倍
-
当然也可以使用
reserve()
函数指定最开始数组容量,假如存储的数据有点多,最开始系统会频繁进行扩容,所以会损耗性能,那么就可以一开始就给数组分配一块空间,若是最开始指定的空间存满了,后续还要存储数据,那么就按照当前容量的2倍进行扩容vector<int> vec; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; // 预留数组容量大小为10,只有capacity大小是10 vec.reserve(10); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; for (int i = 0; i < 11; ++i) { vec.push_back(i); } cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
运行结果:
size = 0, capacity = 0 size = 0, capacity = 10 size = 11, capacity = 20
-
如果你真的认为所有时刻都是2倍扩容,那就错了,使用
insert
插入数据扩容机制就发生了一些改变!vector<int> vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); for (auto elem : vec) { cout << elem << " "; } cout << endl; cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl; auto it = vec.begin() + vec.size(); cout << "insert two datas!, datas num > (capacity - size) && < capacity" << endl; vec.insert(it, 2, 7); cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl; cout << "insert six datas!, datas num >= capacity" << endl; it = vec.begin() + vec.size(); vec.insert(it, 6, 9); cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
// 运行结果 1 2 3 vector.size = 3, vec.capacity = 4 insert two datas!, datas num > (capacity - size) && < capacity vector.size = 5, vec.capacity = 6 insert six datas!, datas num >= capacity vector.size = 11, vec.capacity = 11
注意观察插入元素与当前数组剩余空间与数组容量之间的关系!不难得出:
- 当使用
insert
插入元素数量大于当前数组剩余空间时,且小于数组总容量时,会按照当前size*2
的方式扩容 - 当使用
insert
插入元素数量大于等于容量时,会按照size+当前插入元素的个数
扩容!
- 当使用
vector缩容
-
如果想要节约空间,而放弃效率的话还可以使用
shrink_to_fit();
来移除未使用的容量vec.shrink_to_fit(); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
-
节约空间还可以使用成员函数
swap()
来交换两个数组的数据vector<int> vec; cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; vec.reserve(10); cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; for (int i = 0; i < 11; ++i) { vec.push_back(i); } cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; { //在块作用域创建的vec1是临时的变量,当离开作用域自动被销毁 vector<int> vec1(vec.size()); vec1.swap(vec); //副作用:交换后vec数据没了 } cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
运行结果:
size = 0, capacity = 0 size = 0, capacity = 10 size = 11, capacity = 20 size = 11, capacity = 11
vector迭代器失效问题
问题一
在进行insert的时候,由于插入的元素个数不确定,在插入的过程中会进行扩容,而迭代器还指向旧的空间,所以迭代器已经失效。解决方案,每次操作之前,将迭代器重新置位!
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
auto it = vec.begin();
cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
for (; it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
vec.insert(it, 2, 6);
cout << "insert into 2 datas!" << endl;
cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
// 迭代器失效,打印脏数据
for (; it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
// 正确方式
for (auto it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
问题二
在erase的时候,删除一个元素,会导致后面所有的元素都向前移动,此时迭代器会指向待删除元素的后一个元素,此时也是迭代器失效的一种形式,如果此时将迭代器偏移,会导致有些元素删除不干净!
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(2);
vec.push_back(2);
vec.push_back(5);
for (auto it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
for (auto it = vec.begin(); it != vec.end(); ++it)
{
// 删除数组中元素为3的元素
if (2 == *it)
{
vec.erase(it);
}
}
for (auto it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
运行结果:
1 2 2 2 5
1 2 5
可以看到三个2只删除了两个!
如果本文对你有帮助,记得一键三连哦,一键三连笑哈哈,代码能力顶呱呱!
本人能力有限,如有错误,望不吝指正;原创不易,欢迎转载,转载请注明出处!