HOME/CPP Primer/

3.3 标准库类型vector

Article Outline
TOC
Collection Outline

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