序言
在C语言编程中经常遇到这种情况:没有指定输入数据长度,只能定义很大的数组来存储数据,而且还不方便动态扩充数组大小。
C++中vector容器的存储机制就比较灵活地解决了这个问题,本文就来学习一下vector容器的常见用法。
1. 容器及vector容器
容器
容器是容纳某种数据结构的场所。
如常见数据结构:数组 + 链表 + 队列 + 树 + 栈 + 哈希表 + 集合set + 映射map等
容器分类
- [1] 序列式容器。类似C里面所说的顺序存储结构
- C++序列式容器包括:array(build-in) + list + stack + queue + deque + priority-queue
- [2] 关联式容器。类似C里面所说的非顺序存储结构
- C++关联式容器八廓:set + map + multiset + multimap + RB-tree等
- [1] 序列式容器。类似C里面所说的顺序存储结构
vector容器
- STL提供的一种序列式容器,元素线性排列但未必有序
2. vector容器内存分配
vector容器与数组的区别
- [1] 灵活性:
- 数组(静态 + 动态)每次只能分配一定大小的空间,不方便动态扩充
- vector容器是动态空间,原空间不够用会自行扩充
- [2] 效率:
- 数组是面向用户的,即“分配新空间 + 复制元素 + 释放旧空间”的操作需要用户自己完成
- vector也要经过“分配新空间 + 复制元素 + 释放旧空间”的操作,用户不需要亲自处理空间运用问题,更加安全高效
- [1] 灵活性:
vector容器内存分配
分配策略:以最小的代价连续存储元素。
分配原则:按照《STL源码剖析》中提供的vector源码,vector的内存配置原则为
- 如果vector原大小为0,则配置1,也即一个元素的大小。
- 如果原大小不为0,则配置原大小的两倍。
(1)为了vector容器实现快速的内存分配,其实际分配的容量要比所需的空间多一些,这样不必每次都为新元素进行一次内存分配
(2)当元素超过容器capacity,此时再加入元素时vector的内存管理机制便会扩充容量至两倍,以此扩充至足够容量。
(3)容量扩张必须经历“重新配置、元素移动、释放原空间”这个浩大的工程
3. vector容器基本操作
- [1] 头文件及初始化
//头文件:#include<vector>
//声明及初始化:
vector<int> a; /* 声明一个int类型变量 */
vector<int> a(10); /* 声明一个初始大小为10的变量 */
vector<int> a(10,1); /* 声明一个大小为10,初值为1的向量 */
vector<int> b(a); /* 声明并用变量a初始化变量b */
vector<int> b(a.begin(), a.begin() + 3); /* 将向量a的0,1,2三个元素作为向量b的初始值 */
int arr[] = {1, 2, 3, 4, 5, 6};
vector<int> a(n, n + 5); /* 将数组arr的前5个元素作为向量ad哦初始值 */
vector<int> a(&n[2], &n[4]); /* 将n[2]到n[4]的元素作为a的初值 */
vector<vector<int>> a(10, vector<int>(5)); /* 创建一个int型二维数组,相当于a[10][5] */
- [2] 容量操作
vector<int> a;
a.size(); //向量大小
a.resize(); //更改向量大小
a.capacity(); //向量容量
a.empty(); //向量判断是否为空: 为空返回ture,否则返回false; 函数原型bool empty() const;
a.shrink_to_fix(); //减小向量大小到满足所占存储空间大小: 该语句跟在确定vec.size()之后,与内联函数一样,只是提出修改内存请求,是否实现编译器说了算
- [3] 元素访问
vector<int> a;
a.front(); //返回第一个元素
a.back(); //返回最后一个元素,不检查这个数据是否存在
a[1]; //下标访问,并不会检查是否越界
a.at(1); //at方法访问。at会检查是否越界,是则抛出out of range异常
- [4] 元素修改
vector<int> a;
a.assign(5, 10); //往a里放5个10
a.push_back(element); //在尾部加入一个数据
a.insert(a.begin(), 1); //在a.begin()之前加入1,函数原型: a.insert(pos,elem); 在pos位置插入一个elem的拷贝,返回插入的值的迭代器
a.insert(v.begin(),2,20); //a.insert(pos,n,elem)在pos位置插入n个elem的数据,无返回值
v.insert(v.begin(),v1.begin(),v1.begin()+2);
//a.insert(pos,beg,end)在pos位置插入v1在[beg,end)区间的数据,无返回值
a.pop_back(); //删除最后一个数据
a.erase(a.begin()); //将a/begin()的元素删除:a.erase(pos)删除pos位置的元素,返回下一个元素的位置
a.erase(a.begin() + 1, a.end()); //将第二个元素之后的元素均均删除。 a.erase(begin,end),删除[begin, end)区间内的数据,返回下一个数据的位置
a.clear(); //移除容器内所有数据
- [5] 迭代器
vector<int> a;
a.begin(); //开始指针
a.end(); //末尾指针:指向最后一个元素的下一个位置
a.cbegin(); //指向常量的开始指针。意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
a.cend(); //指向常量的末尾指针。不能通过指针修改所指内容
- [6] 遍历元素
vector<int> a;
//方式1:像数组一样以下标访问
for(int i = 0; i < a.size(); i++){
cout << a[i];
}
//方式2:以迭代器访问
vector<int>::iterator it;
for (it = a.begin(); it != a.end(); it++)
{
cout<<*it<<endl;
}
- [7] 元素翻转
#include <algorithm>
...
reverse(a.begin(), a.end());
- [7] 排序
#include <algorithm>
...
sort(a.begin(), a.end()); //默认按升序排序,即从小到大。
/* 如果想按降序排序 */
//方式1:可先sort()升序排序,再调用reverse()进行翻转
//方式2:采用与C语言中qsort()类似的方式
bool compare(const int &a, const int &b)
{
return a > b; //'>'是降序
}
sort(a.begin(),a.end(),compare);
//qsort()使用区别:compare返回值必须为int,参数必须是const void *,'>'表示升序,'<'表示降序,与sort()中相反
int compare(const void *value1, const void *value2)
{
return *(int *)value1 - *(int *)value2 > 0 ? 1 : -1; //这样是升序排序
return *(int *)value1 - *(int *)value2 > 0 ? -1 : 1; //这样是降序排序
}
qsort(arr, num, sizeof(arr[0]), compare);
Acknowledgements:
http://www.cnblogs.com/QG-whz/p/4558147.html
http://www.cnblogs.com/zhonghuasong/p/5975979.html
http://www.cnblogs.com/YJthua-china/p/6550960.html
http://www.cnblogs.com/scandy-yuan/archive/2013/01/07/2849735.html(推荐)
2017.09.30