3.3 标准库类型vector
3.3 标准库类型vector
标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。因为vector“容纳着”其他对象,所以它也常被称作容器。
要想使用vector,必须包含适当的头文件。
#include <vector>
using std::vector;
C++语言既有类模板,也有函数模板,其中vector是一个类模板。模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化,当使用模板时,需要指出编译器应把类或者函数实例化成何种类型。
对于类模板来说,我们通过提供一些额外信息来指定模板到底实例化成什么样的类,需要提供哪些信息有模板决定。提供信息的方式总是这样:即在模板名字后面跟一对尖括号,在括号内放上信息。
以vector为例,提供的额外信息是vector内所存放对象的类型:
vector<int> ivec; // ivec保存int类型的对象
vector<Sales_item> Sales_vec; // 保存Sales_item 类型的对象
vector<vector<string>> file; // 该向量的元素是vector对象
vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。
3.3.1 定义和初始化vector对象
初始化vector对象的方法 | |
---|---|
vector<T> v1 | v1是一个空vector,它潜在的元素是T类型的,执行默认初始化 |
vector<T> v2(v1) | v2中包含有v1所有元素的副本 |
vector<T> v2 = v1 | 等价于v2(v1),v2中包含有v1所有元素的副本 |
vector<T> v3(n, val) | v3包含了n个重复的元素,每个元素的值都是val |
vector<T> v4(n) | v4包含了n个重负地执行了值初始化的对象 |
vector<T> v5{a, b, c...} | v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vector<T> v5={a, b, c...} | 等价于v5{a, b, c...} |
列表初始化vector对象
vector<string> articles = {"a", "an", "the"};
创建指定数量的元素
可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象:
vector<int> ivec(10, -1); // 10个int类型的元素,每个都被初始化为-1
vector<string> svec(10, "hi!"); // 10个string类型的元素,每个都被初始化为“hi!”
值初始化
通常情况下,可以只提供vector对象容纳的元素数量而略去初始值。此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。这个初值有vector对象中元素的类型决定。
如果vector对象的元素是内置类型,比如int,则元素初始值自动设为0。如果元素是某种类类型,比如string,则元素由类默认初始化:
vector<int> ivec(10); // 10个元素,每个都初始化为0
vector<string> svec(10); // 10个元素,每个都是空string对象
这种初始化的方式有两个特殊限制:其一,有些类要求必须明确提供初始值,如果vector对象中元素的类型不支持默认初始化,我们就必须提供初始的元素值。其二,如果只提供了元素的数量而没有设定初始值,只能使用直接初始化:
vector<int> vi = 10; // 错误:必须使用直接初始化的形式指定向量大小
这里的10是用来说明如何初始化vector对象的,我们用它的本意是想创建含有10个值初始化了的元素的vector对象,而非把数字10“拷贝”到vector中。
列表初始值还是元素数量?
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号还是圆括号。
vector<int> v1(10); // v1有10个元素,每个的值都是0
vector<int> v2{10}; // v2有1个元素,该元素的值是10
vector<int> v3(10, 1); // v3有10个元素,每个的值都是1
vector<int> v4{10, 1}; // v4有2个元素,值分别是10和1
3.3.2 向vector对象中添加元素
push_back函数负责把一个值当成vector对象的尾元素“压到”vector对象的“尾端”。
vector<int> v2;
for(int i = 0; i != 100; i++)
v2.push_back(i);
// 循环结束后v2有100个元素,值从0到99
需要实时读入数据然后将其赋予vector对象:
string word;
vector<string> text;
while(cin >> word)
{
text.push_back(word);
}
向vector对象添加元素蕴含的编程假定
必须确保所写的循环正确无误,特别是在循环有可能改变vector对象容量的时候。
如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环。
3.3.3 其他vector操作
vector支持的操作 | |
---|---|
v.empty() | 如果v不含有任何元素,返回真;否则返回假 |
v.size() | 返回v中元素的个数 |
v.push_back() | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v中第n个位置上元素的引用 |
v1 = v2 | 用v2中的元素拷贝替换v1中的元素 |
v1 = {a, b, c...} | 用列表中元素的拷贝替换v1中的元素 |
v1 == v2 | v1和v2相等,当且仅当它们的元素数量相同且对应文职的元素值都相同 |
v1 != v2 | v1和v2不相等 |
<, <=, >, >= | 以字典顺序进行比较 |
访问vector对象中元素的方法和访问string对象中字符的方法差不多,也是通过元素在vector对象中的位置。例如,可以使用范围for语句处理vector对象中的所有元素:
vector<int> c{1, 2, 3, 4, 5, 6, 7, 8, 9};
for(auto &i : v)
i *= i;
for(auto i : v)
cout << i << " ";
cout << endl;
计算vector内队形的索引
使用下标运算符能获取到指定的元素。
以10分为一个分数段统计成绩的数量:09,1019,...,90~99,100
vector<unsigned> score(11, 0);
unsigned grade;
while(cin >> grade)
{
if(grade <= 100)
++score[grade/10];
}
不能用下标形式添加元素
vector<int> ivec; // 空vector对象
for(decltype(ivec.size()) ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // 严重错误:ivec不包含任何元素
for(decltype(ivec.size()) ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // 正确:添加一个元素,该元素的值是ix