6. C++ vector容器的使用
1. 简介
Vector类 是在 java 中可以实现自动增长的对象数组,vector在C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。 ---百度百科
2. 场景实例
在C语言编程中,涉及到声明一个全局数组的时候,经常会遇到一种情况,数组的大小设定多少合适。一般情况下我们会设定一个比较大的值,例如1000甚至更大,主要就是怕在代码运行中,用到该数组时,若数组容量不够,出现越界的情况,从而导致宕机等较为严重的问题。但是设置过大,也会导致内存浪费,虽然不是什么大问题,但这种变量若定义过多,也会导致一笔不小的开销。在C语言中,可以通过动态数组来解决这一问题。但是在一些场景中,用起来较为复杂。
通过实际例子说明问题,声明一个结构体中,其中包含一个数组成员。
/* 因为无法确定name长度,不得不设定一个不可超越的较大值 */
#define NAME_LENGTH_MAX 100
struct STUDENT {
char years;
long id;
char name[NAME_LENGTH_MAX];
};
在此实例中,我们可以发现,结构体STUDENT包含不确定长度的name成员,为了避免代码访问name数组越界,不得不声明一个不可超越的长度。这就会造成资源浪费的情况。一般情况下,可采用下面两种方法优化。
优化1:C语言中采用动态数组优化
struct STUDENT {
char years;
long id;
char *name;
};
struct STUDENT std;
在用到std变量时,必须对std的name成员分配空间,否则会出现野指针问题,在运行时导致机器宕机。可通过malloc等函数对name进行分配空间。使用起来较为复杂。
优化2:C++ vector
struct STUDENT {
char years;
long id;
vector<char> name;
};
用到std变量时,同样需要对name初始化,可采用vector提供的接口push_back赋初值即可。较优化1方案,使用起来方便一些。
3. 基本用法
C++ 中的vector实现实用的接口供开发者选择:
//头文件
#include <vector>
using namespace std;
/* 构造方法 */
vector<类型>标识符
vector<类型>标识符(最大容量)
vector<类型>标识符(最大容量,初始所有值)
vector(const vector&) 复制vector
vector(begin,end) 复制数组begin到end之间的值到vector
/* E.g.几种较为常用的方法 */
vector<int> num; //int num[]
vector<char> num(5); //char num[];
vector<char> num(5, 1); //char num[] = {1,1,1,1,1};
int num[5]={1,2,3,4,5};
vector<int>num_copy(i+2,i+4); //int num_copy[] ={3, 4, 5};
vector< vector<int> >num; //二维向量. 这里最外的<>要有空格。否则在比较旧的编译器下无法通过 int [][];
/* 常用接口 摘自 https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html*/
1.push_back 在数组的最后添加一个数据
2.pop_back 去掉数组的最后一个数据
3.at 得到编号位置的数据
4.begin 得到数组头的指针
5.end 得到数组的最后一个单元+1的指针
6.front 得到数组头的引用
7.back 得到数组的最后一个单元的引用
8.max_size 得到vector最大可以是多大
9.capacity 当前vector分配的大小
10.size 当前使用数据的大小
11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值
12.reserve 改变当前vecotr所分配空间的大小
13.erase 删除指针指向的数据项
14.clear 清空当前的vector
15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)
16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)
17.empty 判断vector是否为空
18.swap 与另一个vector交换数据
4. 实例
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
struct STUDENT {
char years;
long id;
vector<int> name;
};
void show(vector<int> &vec)
{
cout <<"Value: ";
int num = 0;
for (auto i = vec.begin(); i != vec.end(); i++) {
cout << vec.at(num++) << " ";
}
cout << endl;
}
int main()
{
struct STUDENT stud;
vector<int> tmp(3, 5) ; //int tmp[] = {5, 5, 5};
tmp.push_back(23);
cout <<"---- 1st: ";
show(tmp);
tmp.insert(tmp.begin(), 2, 10);
cout <<"---- 2nd: ";
show(tmp);
tmp.pop_back();
cout <<"---- 3rd: ";
show(tmp);
cout <<"---- 4th: " << tmp.capacity()<<endl;
tmp.clear();
cout <<"---- 5th: ";
show(tmp);
return 0;
}
输出:
---- 1st: Value: 5 5 5 23
---- 2nd: Value: 10 10 5 5 5 23
---- 3rd: Value: 10 10 5 5 5
---- 4th: 6
---- 5th: Value:
5. 注意
在实际使用中,vector不当的用法可能会导致较为严重的问题:
-
在使用vector变量时,必须对其初始化赋值才可以使用。若结构体含有vector成员,也必须先对此成员初始化,否则使用时可能会导致dump。
-
在结构体有vector成员,不可用memset对此结构体进行清零,否则会导致内存泄漏。
按我的理解,C++中的vector 与 C中的 指针 + malloc 类似。只不过vector将动态数组的一系列操作都封装成标准库的接口。如果我们将vector理解成*vector + malloc ,就能够分析出以上问题的原因:
-
第一个问题理解。vector是一个指针,如果没有初始化赋值,就是一个野指针。那么在使用没有初始化的vector,就如同在使用野指针,会导致宕机。
-
第二个问题理解。在初始化后,vector就是一个指针指向了一篇连续的内存,如果使用memset对含有vector成员的结构体清零,那么就会改变vector的指向对象,那么之前的内存还没有释放就被丢失掉了,导致内存泄漏。
6. 总结
c++中vector解决了C语言中声明数组时无需确定范围,其实现方式类似于C语言中的动态数组。不过较C++实现了vector的各种接口,其接口功能看起来也比较符合动态数组的功能。