本文是写给那些,会使用vector,但是对vector的细节,了解不多的人。所以对于vector基本的操作如insert(),push_back(),emplace(),begin(),clear(),此处不再解释。本文重点介绍:vector的结构,vector的动态扩容机制,vector的使用场景,还有一些注意事项。
vector的结构:vector即动态数组,它的动态,基于它的动态扩容机制。本质上它还是一个数组,是连续存储在内存空间中的,他在内存中的结构如下:
图2 vector 的三个指针,分别指向vector的三处关键节点
vector内部有三个迭代器维护着vector的缓存区域:
-
_M_start:它指向vector缓存区的第一个对象。
-
_M_finish:它指向vector缓存区中已插入的最后一个对象的下一个内存块。
-
_M_end_of_storage,它指向vector缓存区的末端(指向的是已分配内存的下一个块)。
vector扩容,获取大小,获取容量,判空,这些基础操作,都是根据这三个迭代器设计的。
_M_finish和 _M_end_of_storage指向的内存块是一样的时候,就会触发内存扩容。
vector的动态扩容:_M_finish和 _M_end_of_storage指向的内存块是一样的时候,也就是当 vector 的大小和容量相等(size==capacity)时,再向其添加对象,那么 vector 就需要扩容。vector 容器扩容的过程需要经历以下 3 步:
-
依据当前对象的个数,重新申请更大的内存空间(依据不同的编译器,可能为当前对象的1.5倍或者2倍);
-
将旧内存空间中的数据,按原有顺序移动到新的内存空间中(拷贝);
-
将旧的内存空间释放。
这也就解释了,为什么 vector 容器在进行插入后,与其相关的指针、引用以及迭代器可能会失效的原因。原先指向的空间已经被释放了。那么指针或者引用也就会失效。
同时可以预测,vector 扩容非常耗时的。为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间,以便后期使用。同时这个内存空间又不能过大,造成申请到的内存,迟迟得不到利用,造成内存的浪费。一个比较合理的值是1.5倍。具体为何选择1.5作为增长因子。在此处有详细的解释:(14条消息) STL中vector 扩容为什么要以1.5倍或者2倍扩容?bryant-xw的博客-CSDN博客vector扩容为什么是两倍
vector的使用场景:由vector的结构特点:简单,顺序存储。决定了他查找的效率较优,而插入删除的效率较差,因为插入删除会造成多个对象的移动。因此它适用于:在某一特定范围内,经常进行查询,但是很少对这个范围进行扩大或减小。
比如:当班里有100个人,我们需要不断查看这100个人的某项数据,这时我们使用vector最好。因为这就是相对于某个范围。
注意事项: 1.vector insert() 与emplace()对比:vector容器提供了 insert() 和 emplace() 这 2 个成员函数,用来实现在容器指定位置处插入对象。总体来说,insert()支持的插入方式更多,功能更加强大(insert()底层调用了emplace()),功能如下所示。而emplace()一次只能插入一个对象,但是支持直接传入classT的构造函数的参数,在要插入的位置直接构造对象。
insert()insert() 函数的功能是在 vector 容器的指定位置插入一个或多个对象。该函数的语法格式有多种:
| 语法格式 | 用法说明 |
|---|---|
| iterator insert(pos,elem) | 在迭代器 pos 指定的位置之前插入一个新对象elem,并返回表示新插入对象位置的迭代器。 |
| iterator insert(pos,n,elem) | 在迭代器 pos 指定的位置之前插入 n 个对象 elem,并返回表示第一个新插入对象位置的迭代器。 |
| iterator insert(pos,first,last) | 在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有对象,并返回表示第一个新插入对象位置的迭代器。 |
| iterator insert(pos,initlist) | 在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个对象,中间有逗号隔开)中所有的对象,并返回表示第一个新插入对象位置的迭代器。 |
下面的例子,演示了如何使用 insert() 函数向 vector 容器中插入对象:
#include#include using namespace std; class Example{ public: Example(int x,int y):a(x),b(y){ cout<<"构造函数"< v; auto it=v.begin(); Example e(3,2); Example x(3,2); vector arr={e,x}; cout<<"insert(pos,elem):"< emplace() emplace() 用于在 vector 容器指定位置之前插入一个新的对象。
该函数的语法格式如下:
1.iterator emplace (const_iterator pos, args...);
其中,pos 为指定插入位置的迭代器;args... 表示与新插入对象的构造函数相对应的多个参数,args可以只填入一个对象本身,那么他会直接将该对象在指定位置拷贝;该函数会返回表示新插入对象位置的迭代器。关于args...,使用了C++11中的变参模板,此处不介绍。感兴趣的同学可以自行查找。
2.vector的初始化初始化时需要注意,初始化后的vector,他的size==capcity。即他的每一个对象都非空。此时要给他增加对象,执行insert()或者emplace(),都会引起扩容。
vector3.vector访问指向迭代器的对象vec1;//不带参数的构造函数初始化。 vector vec2(5);//带参数的构造函数初始化。 vector vec3(5,1);//带参数,且设置初始值的构造函数初始化。 vector vec4={1,2,3};//通过初始化列表初始化。 int arr[5]={1,2,3,4,5}; vector vec5(arr,arr+5);//通过数组地址初始化。 vector vec6(vec5);//通过同类型的vector初始化。 vector vec7(vec6.begin(),vec6.end());//通过同类型vector的指定范围初始化(指定迭代器)。 //补充:二维数组初始化 int length=5; int width=4; vector >vec8(width,vector (length,0));//第二个参数称为匿名对象,他的生存周期,仅仅在这一行语句中 vectorv(10); auto it=v.begin(); cout<<(*it).a< 通过迭代器访问对象,需要使用解引用符*,如果对象非内置类型,那么还需要加上()以访问对象的成员。
4.vector的迭代器失效vecto迭代器是原生态类型的指针,迭代器失效,可以认为是指针失效,如果指针失效则是指针指向了一段非法的空间,说明该空间已经被释放了。由此可以推出是vector中的空间改变了引起的迭代器失效,那么便是vector中的接口操作改变了vector中的空间,在此归类出三种可能会引起迭代器失效的操作:
(1) 扩容操作可能会引起迭代器失效: vector中涉及到扩容的接口方法:resize() / reserve() / push_back() / insert() / assign() /emplace()/emplace_back()、下面演示insert()方法引起迭代器失效:
引起迭代器失效:
(2)vector之中的删除方法:erase()/pop_back()。下面演示erase()方法引起迭代器失效:
int main() { vectorv(2); auto it=v.end(); Example e(2,3); v.erase(it); cout<<(*it).a< 引起迭代器失效:
(3)swap()/clear(),也会引起迭代器失效。
解决迭代器失效:
在有可能引起迭代器失效的位置,重新对迭代器进行赋值。
int main() { vectorv(2); auto it=v.end(); Example e(2,3); v.erase(it); it=v.end(); cout<<(*it).a< 小技巧:
当时用 erase(it++)删除it指向的对象时,迭代器会指向下一个对象,不会失效。
这是因为,后缀形式++运算符的内部实现类似:
const int int::operator++(int) //函数返回值是一个非左值型的,与前缀形式的差别所在。 {//函数带参,说明有另外的空间开辟 int oldValue = *this; (*this)+=1; // 增加1 return oldValue; // 返回old的值 }它使用一个临时变量保存了原先的迭代器,再将it后移一位,并返回这个临时变量,这样删除该元素,不会使这个迭代器失效,因为这个时候它已经指向了下一个对象。
这样便可以解决迭代器失效的问题。
本blog的部分图片,文字参考:
C语言中文网:C语言程序设计门户网站(入门教程、编程软件)
vector 容器初始化的几种方式_xixi up!的博客-CSDN博客_vector容器初始化
vector迭代器失效_小明学编程~的博客-CSDN博客



