vector是STL里常见的容器,虽然和数组一样是顺序容器,但是它可以随着元素的插入不断地扩增自己的容量
vector扩增容量的机制
先看代码
代码中的size()返回vector的大小(也就是目前有多少个元素),而capacity()则返回容器的容量(再次扩容前最多可以容纳的元素个数)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a = { 1,2,3,4,5,6,7,8,9 };
cout << a.size() << endl;
cout << a.capacity() << endl;
a.push_back(0);
cout << a.size() << endl;
cout << a.capacity() << endl;
}
结果为(前两行为扩容前)
vs2019
devcpp
可以看见原来有9个元素的vector大小(size)容量(capacity)都为9,那么说明目前这个vector已经满了,如果我们执行a.push_back(0);(在尾部插入一个整型0)那么vector就需要进行扩容,从结果的后两行可以看出,vector的容量都变大了,devcpp的容量翻倍,而vs2019增加了4(一般vector扩容会申请比原来容量多一倍的空间,就和devcpp一样,但是我也不知道为什么vs2019不是,可能它是内存管理大师吧。。。)
而vector的扩容并非简单的在容器尾部申请空间然后存储新的元素,vector的扩容是会申请一个新的空间,然后把原有的元素拷贝过去,再释放掉原有的空间。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a = { 1,2,3,4,5,6,7 };
int* p = &a[0];
a.push_back(8);
int* q = &a[0];
cout << "扩容前:" << p << endl;
cout << "扩容后:" << q << endl;
}
结果为
由此可见容器的首元素地址不一样了,容器整体搬家了
转载:vector的数据结构
vector数据结构,采用的是连续的线性空间,属于线性存储。他采用3个迭代器_First、_Last、_End来指向分配来的线性空间的不同范围,下面是声明3个迭代器变量的源代码。
template<class _Ty, class _A= allocator< _Ty> >
class vector{
...
protected:
iterator _First, _Last, _End;
};
_First指向使用空间的头部,_Last指向使用空间大小(size)的尾部,_End指向使用空间容量(capacity)的尾部。例如
int data[6]={3,5,7,9,2,4};
vector<int> vdata(data, data+6);
vdata.push_back(6);
...
vector初始化时,申请的空间大小为6,存放下了data中的6个元素。当向vdata中插入第7个元素“6”时,vector利用自己的扩充机制重新申请空间,数据存放
结构如图1所示:
简单描述一下。当插入第7个元素“6”时,vector发现自己的空间不够了,于是申请新的大小为12的内存空间(自增一倍),并将前面已有数据复制到
新空间的前部,然后插入第7个元素。此时_Last迭代器指向最后一个有效元素,而_End迭代器指向vector的最后有效空间位置。我们利用vector的成员函数size
可以获得当前vector的大小,此时为7;利用capacity成员函数获取当前vector的容量,此时为12。
转载于:
迭代器iterator的使用
既然vector在插入元素时会扩容,扩容就会改变自身数据的地址,那么在使用iterator需要特别小心,否则就会产生严重错误
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a = { 5,6,7,8 };
for (vector<int>::iterator i=a.begin();i<a.end();++i)
{
int j;
cin >> j;
a.push_back(j);
}
return 0;
}
这段代码在devcpp能过编译且能正常输入并结束程序,而vs2019则会引发错误
这段代码根本就是错的,我们来理解一下这段代码
(有的人可能会想,是不是因为我们一直在插入元素导致i不能和尾部指针相遇导致程序陷入死循环,并不是陷入死循环的问题,就算是,我们也可以用ctrl+Z关闭cin,不让容器插入元素来结束程序)
一开始for循环里定义了一个迭代器i,它指向容器里的第一个元素,也就是5,循环判断条件是i<a.end()(.end()表示容器最后一个元素的下一个地址)图中黄块8的右边白块,.end()返回它的地址,.begin()返回黄块5的地址
循环改变条件是++i,也就是每次迭代器向右移动一位,而前文我们可以知道,创建好的vector容器一开始就是满的,在第一次插入时就需要扩容,扩容就导致原有的容器元素集体搬家(在内存中的地址集体改变),其中.end()返回的地址也会改变,这也就导致了个问题:
我们一开始定义的,i=a.begin(),没有改变,还停留在原来的地方,所以想要通过++i来结束循环几乎是不可能的
为了防止程序员犯错,c++引出了一个机制:
迭代器失效
容器扩容后原先定义的迭代器都会失效,使用迭代器必须重新定义(在循环中使用的容器必须特别注意迭代器失效的问题)