概述
vector 是一种C++标准模板库STL中定义的一种序列容器,它允许你在运行时动态地插入和删除元素。
vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你可以添加任意多的元素在其中,并且你不需要手动分配和释放内存。
它同时具有动态类型和数组的特性:
动态大小:内存空间可以根据需要自动增长和缩小。
连续存储:元素在内存中是连续存储的,这使得访问元素非常快速。
可迭代:可以被迭代,你可以使用循环来访问它的元素。
元素容器:可以存储任何类型的元素,包括基本类型、对象、指针等。
接下来,我们介绍vector类型定义的全体成员函数。
容器创建
STL库提供了以下方式创建一个vector。
以T表示任意类型。
无参构造 | vector<T>vec(); | 生成一个存储T类型的动态数组vector。 |
提供初始空间的构造 | vector<T>vec(int num); | 生成初始长度为num的vector,初值为0。 |
提供初始长度和初值的构造 | vector<T>(int num,T target); | 生成初始长度为num的vector,初值为target。 |
提供源数据地址的构造 | vector<T>vec(T*begin,T*end); vector<T>vec(vector<T>::iterator begin,vector<T>::iterator begin); | 从定长数组begin到end之间获取数据初始化。 从vector容器的迭代器(容器指针)之间初始化。 |
拷贝构造 | vector<T>vec(vector<T>another); | 将another整体赋值给新vector。 |
vector<int>a;//{}
vector<int>b(3); //{0,0,0,}
vector<int>c(3,5);//{5,5,5,}
int x[] = {1,2,3,4,5,6};
vector<int>d(x,x+6);//{1,2,3,4,5,6,}
vector<int>vec(d);//{1,2,3,4,5,6,}
数据控制
vector类型通过成员函数维护数据。
尾部压入 | push_back(T elem); | 在尾部加入元素 |
尾部弹出 | pop_back(); | 删除尾部最后一个元素 |
插入 | insert(vector<T>::iterator it,T elem); | 在迭代器it(后文提及)指向的位置加入元素,原元素依次向后移动 |
删除 | erase(vector<T>::iterator it); | 删除it指向的元素 |
交换 | swap(vector<T>&another); | 交换两个容器的元素 |
清空 | clear(); | 清空元素 |
再分配 | assign();参数与后三种构造相同 | 重新初始化vector,方法与后三种构造函数一致 |
尾部安置(C++11) | emplace_back(T elem); | 由C++11后可用,在尾部原地构造一个元素,作用与push_back()相同,但没有赋值过程而是原地生成新元素,所以效率更高 |
头部安置(C++11) | emplace_front(T elem); | 与empalce_back()类似 |
插入安置(C++11) | emplace(vector<T>::iterator it,T elem); | 作用于insert相同,但因为上述原因效率更高 |
数据访问
vector同时提供了类形式的函数访问和数组形式的运算符访问。
重载[]号访问 | operator[](int pos); | 模仿定长数组的数据访问形式,如arr[3]=5; vector因此使用起来与定长数组无异。 |
成员函数式访问 | at(int pos); | 返回pos处元素的引用。它实际与[]号相同。 |
访问头元素 | front(); | 返回头元素。 |
访问尾元素 | back(); | 返回尾元素。 |
直接访问底部(C++11) | data(); | 返回vector维护的底部数组的头地址。 (vector是基于数组这种数据类型的容器,它的所有操作都是在维护他的成员指针指向的定长数组) |
//d的内部:{1,2,3,4,5,6,}
d.at(2) = 7;
d.front() = 9;
d.back() = 8;
for (int i = 0; i < 6; i++)cout << d[i];//输出927458
int* p = d.data();//p得到了头元素d[0]的地址
cout << *p;//输出9
迭代器
迭代器是c++STL库提供的指向容器的类型,有着类似指针的作用。
通常定义vector<T>::iterator it=.....来获得一个指向某处的it迭代器。
头位置 | begin(); | 返回头位置(vector第一个元素的位置,vector为空时和end相等)。 |
尾位置 | end(); | 返回尾位置。(它指向的位置是第一个不属于vector的位置而不是vector的最后一个元素。) |
常量头位置 | cbegin(); | 返回头位置,赋值给const_iterator类型。这种迭代器不可修改容器。 |
常量尾位置 | cend(); | 返回常量尾位置。 |
逆序头位置 | rbegin(); | 返回逆序头位置,赋值给reverse_iterator类型。指向最后一个元素(与end不同),对逆序迭代器的++操作会使其向头部移动,--向尾部移动(与正序相反) |
逆序尾位置 | rend(); | 返回逆序尾位置,指向vector前面的那个不属于vector的位置(与front不同) |
常量逆序头位置 | crbegin(); | 返回常量逆序头位置,赋值给const_reverse_iterator类型。 |
常量逆序尾位置 | crend(); | 返回常量逆序尾位置。 |
//d的内部:{1,2,3,4,5,6,}
for (vector<int>::const_iterator cit = d.begin(); cit != d.end(); cit++)
cout << *cit;
//输出123456for (vector<int>::const_reverse_iterator crit = d.crbegin(); crit != d.crend(); crit++)
cout << *crit;
//输出654321
内存管理
vector不仅可以自动管理内部空间,还支持程序员手动管理内部空间。
vector内部关于内存同时拥有size和capacity两种参数,为什么?
试想:每次申请长度都只是当前长度+1,则会在内存空间中反复向后利用函数申请新空间来维护数据,这同时耗费了时间和空间。
那么如果每次都申请真实长度size的两倍空间capacity,则每次申请后都有一段空闲区域容纳新元素,此时不需要申请新空间,只有当再次填满时才申请新空间,则减少了时间成本,提高了空间利用率。
获取实际长度 | size(); | 返回vector中的有效元素个数。 |
控制实际长度 | resize(int num); resize(int num,T target); | 将有效元素限制到num个。size>num时抛弃多余部分;num>size则将将多出的位置置为target,无target时置为0. |
获取真实空间大小 | capacity(); | 获取vector维护的底层数组的全部容量。 |
控制真实空间 | reserve(int num); | num>capacity时扩容,否则无事发生。 |
判断是否为空 | empty(); | size==0时返回true,否则返回false。 |
空间收缩 | shrink_to_fit(); | 将capacity收缩至size大小。 |
获取整个程序的极限容纳空间 | max_size(); | 返回整个程序支持的理论最大vector长度。 |
Tips
vector<bool>不是装载bool类型的标准vector容器,这个奇异搞笑的类型不是vector<T>的一个实现,它就叫"vector<bool>",常年被c++标准委员会诟病。它本来是用来演示如何使用STL库的一个小白鼠,存储bool这个bit大小的元素,在一个字节上存了八个。它的运行效率极低,不推荐使用。