标准库类型vector
1.1概述
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和 string 对象一样,标准库将负责管理与存储元素相关的内存。我们把 vector称为容器,是因为它可以包含其他对象。
1.2 头文件
使用 vector 之前,必须包含相应的头文件。后续的例子,都是假设已作了相应的 using 声明:
#include <vector>
using std::vector;
C++语言中既有类模板,也有函数模板。而vector 是一个类模板(class template)。使用模板可以编写一个类定义或函数定义,而用于多个不同的数据类型。因此,我们可以定义保存 string 对象的 vector,或保存 int 值的 vector,又或是保存自定义的类类型对象(如
Sales_items 对象)的vector。
要对C++有了相当深入的理解才能写出模板,我们一开始不需要去创建模板,目前试着使用它就行了。
声明从类模板产生的某种类型的对象,需要提供附加信息,信息的种类取决于模板。以vector 为例,必须说明 vector 保存何种对象的类型,通过将类型放在类型放在类模板名称后面的尖括号中来指定类型:
vector<int> ivec; // ivec保存int类型的对象
vector<Sales_item> Sales_vec; // 保存Sales_items类型的对象
vector<vector<string>> file; //该向量的元素是vector对象
和其他变量定义一样,定义 vector 对象要指定类型和一个变量的列表。上面的第一个定义,类型是 vector<int>,该类型即是含有若干 int 类型对象的vector,变量名为 ivec。第二个定义的变量名是 Sales_vec,它所保存的元素是 Sales_item 类型的对象。
vector 不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector 类型的每一种都指定了其保存元素的类型。因此,vector<int>和 vector<string> 都是数据类型。
1.3.1 vector 对象的定义和初始化
vector 类定义了好几种构造函数用来定义和初始化 vector对象。下表列出了这些构造函数:
vector<T> v1;
v1是一个空vector,他潜在的元素是T类型的,执行默认初始化操作。
vector<T> v2(v1);
v2 为 v1 的一个副本
vector<T> v2=v1;
v2=v1;等价于上面的v2(v1); 即v2 为 v1 的一个副本
vector<T> v3(n, val);
v3包含了n个重复的元素,每个元素的值都为val
vector<T> v4(n);
v4包含了哪个重复地执行了值初始化的对象
vector<T> v5{a,b,c……};
v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a,b,c……};
等价于v5{a,b,c……}
关于上表的讲解:
可以根据如下代码创建一个指定类型的空vector:
vector<T> v1;
看起来空的vector似乎用处不大,但很快我们就会知道程序在运行时可以从中很高效地往vector对象中添加元素。其实,容器最常用的方式便是先定义一个空vector,然后当运行时获取到元素的值,再逐一添加。
当然我们也可以在定义vector的时候指定元素的初值。比如,可以把一个 vector对象复制到另一个 vector 对象中,而新复制的 vector 中每一个元素都是初始化为原 vectors 中相应元素的副本。需要注意此情况下,两个vector对象的类型必须相同:
vector<int> ivec1; // ivec1为int
vector<int> ivec2(ivec1); // 正确的初始化方法: int型的ivec1拷贝给同样是int型的ivec2
vector< int > ivec2= ivec1;// 正确的初始化方法: int型的ivec1拷贝给同样是int型的ivec2
vector<string> svec(ivec2); //错误的初始化方法r: 一个是int,一个是string,不一致不能拷贝
1.3.2 列表初始化方法
C++11的新标准,提供了此列表初始化方法。这种方法其实就是用“花括号”括起来的0个或多个初始元素值赋值给vector对象,把每个元素都一一列举出来:
vector<string>articles={“a”,”an”,”the”};
上述vector包含三个元素:第一个是字符串“a”,第二个是“an”,第三个是“the”。
我们知道,C++语言提供了如下四种不同的初始化方法:
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);
在vector的初始化的大多数情况下,这些方法可以相互等价使用,但是有以下例外:
其一,使用拷贝初始化时(即使用=时),只能提供一个初始值。
其二,如果提供的是一个类内初始值,只能用拷贝初始化或花括号形式初始化。
其三,若提供的是初始化值的列表,只能把初始值都放在花括号里进行列表初始化,而不能放到圆括号里面:
vector<string> v1{"a","an", "the"}; // 使用花括号进行列表初始化,正确
vector<string> v2("a","an", "the"); // 使用圆括号,错误
1.3.3 创建指定数量的元素
可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象:
vector<int> ivec(10, -1); // 初始化10个都为-1的int对象。
vector<string> svec(10,"hi!"); // 初始化10个同为"hi!"的string对象
1.3.3关键概念:vector 对象动态增长
【vector 对象(以及其他标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。】【因为 vector 增长的效率高,在元素值已知的情况下,最好】
这种增长方式不同于 C 语言中的内置数据类型,也不同于大多数其他编程语言的数据类型。具体而言,如果你习惯了 C或 Java 的风格,由于vector 元素连续存储,可能希望最好是预先分配合适的空间。但事实上,为了达到连续性,C++ 的做法恰好相反,即动态地添加元素。
虽然可以对给定元素个数的 vector 对象预先分配内存,但【更有效的方法是先初始化一个空 vector 对象,然后再动态地增加元素】
1.3.4 值初始化
如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化(valueinitializationd)。这个由库生成的初始值将用来初始化容器中的每个元素,具体值为何,取决于存储在vector 中元素的数据类型。
l 若vector为int型,则标准库将用 0值创建元素初始化式。
l 若vector为string,则每个都为空字符串NULL。
vector<string> fvec(10); // 10 个元素,每个元素都为0
vector<string> svec(10); //10 个元素,每个元素都为空字符串NULL
1.3.5 花括号与圆括号:列表初始值还是元素数量?
如果用圆括号,说明是用来构造vector对象的,一般指元素数量,而花括号却表示我们想进行列表初始化该vector对象的值。
范例如下。对于int型:
vector<int> v1(10); // v1有10个元素,初值都为 0
vector<int> v2{10}; // v2 有一个元素,值为10
vector<int> v3(10, 1); // v3 有10个元素,初值都为1
vector<int> v4{10, 1}; // v4 有两个元素,初值分别为10和1
另一方面,如果初始化时使用了花括号的形式但提供的值又不能用来列表初始化,此值就可以用来构造vector对象,代表元素数量。因为列表初始化花括号中的值必须与元素类型相同,所以v7,v8的值不能用作元素的初始值
范例如下。对于string型:
vector<string> v5{"hi"}; //列表初始化: v5有一个元素,值为”hi”
vector<string> v6("hi"); //错误,不能使用字符串字面值构建vector对象
vector<string> v7{10}; // v7 有10个默认初始化的元素
vector<string> v8{10,"hi"}; // v8有10个元素,每个元素都为hi
1.3.2 向vector对象中添加元素
对vector对象来说,直接初始化的方式适用于三种情况:
1. 初始值已知且数量较少
2. 初始值是另一个vector对象的副本
3. 所有元素的初始值都一样。
然而更常见的情况是:创建一个vector时并不清楚实际所需的元素个数,元素的值也经常无法确定,还有些时候即使元素的初值已知,但如果这些值总量较大而各不相同,那么在创建vector对象的时候执行初始化操作也会很繁琐。
9个元素,列表初始化可以胜任。99个,999个,列表初始化就不行了,此时应先创建一个空的vector,然后在运行时再利用vector对象的成员函数put_back向其中添加元素。put_back负责把一个值置为vector对象的尾元素,“压到(push)”vector对象的“尾端(back)。”如:
vector<int> v2; // 空vector
for (int i = 0; i != 100; ++i)
v2.push_back(i); // 依次把整数值放到v2尾端
// 循环结束后v2有100个元素,值分别为0到99
如果直到运行时才知道vector对象中元素的确切个数,也应该使用刚刚的这种方法创建vector对象并为其赋值。例如,有时候需要实时读入数据然后将其赋予vector对象:
//从标准输入读入单词,将其作为vector对象的元素存储
string word;
vector<string> text; //空vector
while (cin >> word) {
text.push_back(word); //添加word到text尾部
}
也就是,先创建一个空的vector,依次读入未知数量的值并保存到text中。
1.3.3 vector 的下标操作
vector 中的对象是没有命名的,可以按 vector 中对象的位置来访问它们。通常使用下标操作符来获取元素。vector 的下标操作类似于 string 类型的下标操作(3.2.3 节)。.
vector 的下标操作符接受一个值,并返回 vector 中该对应位置的元素。
vector 元素的位置从 0 开始。下例使用 for 循环把 vector 中的每个元素值都重置为 0:
// reset the elements in the vector to zero
for (vector<int>::size_type ix = 0;ix != ivec.size(); ++ix)
ivec[ix] = 0;
和 string 类型的下标操作符一样,vector 下标操作的结果为左值,因此可以像循环体中所做的那样实现写入。另外,和 string 对象的下标操作类似,这里用 size_type 类型作为 vector 下标的类型。在上例中,即使 ivec 为空,for 循环也会正确执行。ivec 为空则调用 size 返回 0,并且 for 中的测试比较 ix 和 0。第一次循环时,由于 ix本身就是 0 就是 0,则条件测试失败,for 循环体一次也不执行。
1.3.4 下标操作不添加元素
初学 C++ 的程序员可能会认为vector 的下标操作可以添加元素,其实不
然:
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0;ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has noelements
上述程序试图在 ivec 中插入 10个新元素,元素值依次为 0 到 9 的整数。但是,这里 ivec 是空的 vector 对象,而且下标只能用于获取已存在的元素。
这个循环的正确写法应该是:
for (vector<int>::size_type ix = 0;ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new elementwith value ix
必须是已存在的元素才能用下标操作符进行索引。通过下标操
作进行赋值时,不会添加任何元素。
08-21
08-21
08-21
08-21
08-21