目录
编辑 1.3 范围构造函数(Range Constructor)
1.vector构造
1.1默认构造函数
vector<int>是vector类模版类型,尖括号里的类型是指生成什么类型的vector的类,实质上vector可以看做一个数组,vector<int>实质上就是生成了一个存int类型的数组,而tamp是这个数组的名字。这样默认构造是默认构造了一个空的vector数组,类型不是int,而是vector<int>
因为是尖括号里是模板所以也可以使用别的类型来构造函数,比如string,这样生成的就是string类型的数组了
同样也可以嵌套使用,比如vector<int>构成的是int类型的数组,而尖括号里面是填模板类型,所以vector<int>类型也可以,写成vector<vector<int>>,数组里面存的是数组,这样就构成了一个二维数组
1.2 fill 填充构造函数
原型为
填充构造函数就是用n个类型的数据构造vector
比如vector<int> tamp(5,2);
就是用5个2去构造vector,与默认构造函数相比此时不再是空的vector,而是里面被2填充
因为vector是数组所以可以直接用方括号访问的方式打印一下
除了内置类型,它也可以用自定义类型来填充初始化vector
甚至可以直接这样写,用string匿名对象来构造vector
1.3 范围构造函数(Range Constructor)
范围构造就是利用另一个容器的迭代器来完成构造
比如利用string的迭代器来构造vector
同样也可以通过另一个vector的迭代器来完成构造
1.4拷贝构造函数
就是用另一个vector去直接拷贝构造vector
2.initializer_list初始化vector
initializer_list是C++11提出的东西,其实就是一切都可以{ }中括号初始化
initializer_list到C++11的先关知识点再细讲,目前记住就是用中括号初始化就行,也可以在中括号前加上等号
3.迭代器
vector迭代器与string类似都分为正向迭代器,反向迭代器以及常量迭代器就不细讲了
迭代器组合 | 功能说明 | 迭代器类型 |
begin()+end() | 正向起始迭代器+正向结束迭代器 | iterator |
rbegin()+rend() | 反向起始迭代器+反向结束迭代器 | reverse_iterator |
cbegin()+cend() | 正向常量起始迭代器+正向常量结束迭代器 | const_iterator |
crbegin()+crend() | 反向常量起始迭代器+反向常量结束迭代器 | const_reverse_iterator |
与string类类似,vector也可以看做指针
用范围构造vector来举例,用数组的指针也可以完成构造,所以可以看出来vector的迭代器其实是类似指针的
同样也可以用指针偏移量来完成构造
4.常用的几个成员
4.1 size()统计当前有效字符个数
4.2 capacity vector容器当前空间大小
vs编译器扩容机制是1.5倍左右扩容的,当capacity空间不够又要插入数据时就会发生扩容,而capacity就是查看当前空间大小的函数。
4.3 empty()判空和clear()清空
6.插入和删除
6.1push_back(尾插)pop_back(尾删)
反正就每次都在最后面插入或者在最后面删除,每次都只能操作一个数据
5.遍历
vector遍历与string基本一样,都有数组那样方括号[ ]访问遍历方式,也有用迭代器访问遍历方式,依旧C++11新标准范围for遍历
普通数组方式遍历
迭代器访问遍历
迭代器使用方法和指针差不多,所以解引用就可以直接访问到了,也可以用auto自动推导类型
范围for遍历
范围for其实底层还是迭代器访问方式,只不过它进行了封装处理
也可以直接用auto推导类型
6.2insert和erase
(1)insert
总结一下,第一个插入是用迭代器控制插入位置,然后插入值,第二个与第一个类似,只是这次是插入n个val。第三个是在指定位置插入另一个容器的内容,用迭代器构成区间来控制插入的内容。
与string迭代器对比,其实都差不多,只不过string把字符与字符串单独拿出来讲了
vector在指定位置insert插入单个数据的例子
值得注意的是insert一般是在指定位置之前插入的,所以找到插入位置之后还得往后挪到一个位置才是正确插入位置。比如1 2 3要在2后面插入数据,此时迭代器要指向3才能正确插入数据
iterator insert (iterator position, const value_type& val);iterator position是要插入的位置也就是tamp.begin()+2,const value_type& val是具体要插入的数据,也就是555
vector在指定位置insert插入n个同一数据的例子
void insert (iterator position, size_type n, const value_type& val);在下面的例子中,iterator position是迭代器位置也就是tamp.begin(),size_type n是插入数量也就是下面例子的5,const value_type& val是具体要插入的数据,也就是下面的字符‘h’
vector容器范围插入例子
template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);iterator position依旧是要插入的位置,也就是下面例子中的tamp.begin()+1,InputIterator first, InputIterator last 是插入另一个容器内容的范围,可以插入全部比如下面例子中的 copytamp.begin(), copytamp.end(),如果指向插入另一个容器的部分内容话那么就得用迭代器加偏移量来实现,比如只插入he,那么就改写成copytamp.begin(),copytamp.begin()+2,因为C++是左闭右开的,所以如果你要处理最右边界的那个数字那么迭代器就得指向它后面
迭代加偏移量例子
(2)erase
第一个就是删除指定位置的数据,相比与string的erase还得考虑要删除的长度,vector是数组,删除的是数组多个元素中的一个,所以不需要考虑长度。如果要删除多个数据,那么直接用迭代器范围删除就可以了
迭代器控制删除指定位置数据的例子,与insert相比删除是迭代器指向谁就删除谁,而插入是指向谁就插入在谁的前面
迭代器范围删除的例子,依旧是左闭右开规则所以在处理右边界时要多往后面挪动一位
比如下面的例子,本来begin()+2就已经指向k了,但是因为C++规则所以要往后再挪动一位
7.vector的容量操作
resize函数
resize是用来调整vector容器中有效字符的个数,其实也就是直接调整size
-
如果新大小小于当前大小:
resize
会将容器的大小减少到指定的新大小,并销毁超出该范围的元素。这意味着它会从向量的末尾开始移除元素,直到向量的大小达到指定的新大小。但是,它不会直接“清空”或“删除”那些被移除的元素;而是调用它们的析构函数(对于基本数据类型如char
,这实际上什么也不做,但对于具有非平凡析构函数的类型则很重要)。end
迭代器会相应地向前移动,以反映新的容器末尾。 -
如果新大小大于当前大小:
resize
会增加容器的大小到指定的新大小,并在新添加的元素位置上执行值初始化(对于基本数据类型如char
,这意味着它们会被初始化为0)。这会增加向量的容量(如果需要的话),并移动end
迭代器以指向新的容器末尾。打印扩容之后的数据可以看出来原本没有数据的空间全部填充了0
值得注意的是如果是char类型的数组,那么末尾填充的是‘\0’,\0是不能打印出来的,但是依旧会占据空间
不过你也可以在扩容的地方指定多扩的空间用什么值来填充
reserve函数
reserve与resize相对,reserve调整的是vector的capacity的大小,也就是实际数组的大小,size是有效数据的大小,这两个有区别。如果空间不过而此时要插入数据是需要扩容的,此时扩容改变的是capacity的大小而不是size的大小
可以插入几十个数据来分别看一个capacity的变化情况,以此来了解扩容机制
从上图的例子可以看出vector以原空间1.5倍的大小来进行扩容
不过不同的编译器的扩容机制都不一样,比如Devc++ 它的底层是gcc,扩容机制就和vs编译器扩容情况不一样。从下图可以看出来devc++的vector是以原空间的两倍来进行扩容的
在vs编译器下,reserve空间大小无论是大于size还是小于size都不会进行缩容,依旧保持原来的reserve的空间大小。如果是原先就reserve比size小的空间,那么最终capacity会等于size
原先就reserve比size小的空间的情况
上来就直接缩容是不会做改变的
与string的缩容不同,即使在gcc和g++环境下,vector无论咋样只要reserve了一个大的空间然后再reserve是实现不了的,打印出来依旧是之前大的数值,string在vs编译器下不会缩容,而在gcc环境下会缩容(前提是reserve出来的capacity大于size)
8.vector::at与方括号访问方式
at的使用方式与方括号一致[ ]
你也可以用at来访问遍历元素
at这个函数实际上返回的是数组中对应元素的引用,所以也可以通过at来进行修改数组中的元素
at与[ ]区别在于虽然都会进行边界检查,但是[ ]是直接assert断言处理越界,at是抛异常,异常信息可以打印出来看看是出来什么问题
可以用try去检查是否抛异常然后用catch进行捕获打印异常(这块到异常的时候再细讲)