update:2016/11/16
1.标准库类型vector
vector
能够容纳绝大多数类型的对象作为其元素,但是由于引用并不是对象,因此不存在包含引用的vector
。除此之外,其他大多数(非引用)内置类型和类类型都可以作为vector
的元素,这也包含vector
。
1.定义和初始化vector
vector
作为一个类模板,本身不会指定类型,而必须要为其指定类型。例如:
vector<int> ivec; //保存int类型的对象
vector<Sales_item> Sales_vec; //保存Sales_item类型的对象
vector<vector<string>> file; //保存vector对象的vector
初始化一个vector
对象的常用方法如下:
初始化vector 语句 | 含义解释 |
---|---|
vector<T> v1 | v1 是一个空vector ,执行默认初始化 |
vector<T> v2(v1) | v2 使用v1 的值初始化,包含了v1 的所有元素 |
vector<T> v2=v1 | v2 是v1 的拷贝,包含了v1 的所有元素 |
vector<T> v3(n) | v3 包含了n 个元素,执行了值初始化 |
vector<T> v4(n,val) | v4 包含了n 个重复的元素,每个元素的值都是val |
vector<T> v5{a,b,c...} | v5 的元素按顺序赋予初始值,执行列表初始化 |
vector<T> v5={a,b,c...} | v5 使用列表的拷贝进行初始化。 |
1. 默认初始化
对vector
的默认初始化可以提供一个空的容器,方便之后对其进行任何允许的操作。
vector<int> vector1;
2. 拷贝初始化
对vector
执行拷贝初始化可以在初始化时将一个已存在的vector
元素的值拷贝给新的vector
对象。此时新的vector
中的元素就是原vector
对象对应元素的副本。
//assume that an int vector {1,2,3,4,5} called vector1 has been defined.
//then the following codes are equivalent to each other.
vector<int> vector2=vector1;
vector<int> vector3(vector1);
vector<int> vector4{vector1};
- 拷贝初始化时,两个
vector
对象的类型必须相同。
3. 列表初始化
对vector
执行列表初始化时,可以用花括号{}
将0个或多个初始元素值括起来,使用其在为新的vector
对象初始化的同时为其元素赋予初值。
vector<int> vector5{1,2,5,8,0};
4. 值初始化
对vector
执行值初始化,可以用圆括号()
将vector
容纳的元素数量和所有元素的统一值括起来,使用其在为新的vector
对象初始化的同时,创建指定数量的元素。
在为vector
进行值初始化时,首先由编译器创建一个经过初始化的元素初值,并将其赋给容器中的所有元素。
- 可以将元素值省略,即用括号
()
将vector
对象容纳的元素数量括起来。在缺省元素初值的情况下,元素初值由vector
对象中的元素类型决定:如果vector
对象的元素是内置类型,则元素初始值设为0
;如果元素是某种类类型,则元素由类的默认构造函数进行初始化。总之,在缺省元素初值的情况下,创建一个经过默认初始化的元素初值,并将其赋给容器中的所有元素。
vector<int> vector6(10);
vector<int> vector7(10,12);
5.构造还是初始值列表?
初始化的真实含义依赖于传递初始值时使用的是花括号{}
亦或是圆括号()
:
()
表示使用参数来构造一个vector
对象。{}
表示参数是一个初始值列表。
//the deference between () and {}
vector<int> myVector1(10,3); //10 elements:3,3,3,3,3,3,3,3,3,3
vector<int> myVector2{10,3}; //2 elements:10,3
2.vector
的迭代器
迭代器(iterator)是一种能够检查容器内元素并遍历元素的数据类型。
如同我们使用过的指针,迭代器可以提供对对象的间接访问。事实上,从面向对象的角度而言,迭代器是一个基类,而指针则是这个基类的一个派生类。通过使用迭代器,我们可以在不知道对象内部表示的情况下(间接)按照一定的顺序访问一个容器的所有元素(遍历)。
2.1vector
迭代器的使用
和指针不一样的是,我们可以不必关注元素的地址。vector
在拥有迭代器的同时,还提供了返回迭代器的成员函数(运算符)。
1.begin
和end
begin
成员返回指向头元素的迭代器。
end
成员返回指向尾元素的下一位置的迭代器(尾后迭代器)。
vector<T> myVec = {1, 2, 3, 4, 5}
auto begin = myVec.begin(), end = myVec.end();
- 如果调用
begin()
或end()
的是空容器,则二者都返回尾后迭代器。
2.vector
迭代器支持的运算
我们提到了vector
的迭代器事实上是一种数据类型,于是对于这样的一个数据类型一定支持一定的运算(操作),下面列出vector
的迭代器所有支持的运算(以下iter
代表某个迭代器类型变量):
运算符 | 操作 |
---|---|
*iter | 对迭代器iter 解引用 |
iter->mem | 解引用iter 名为mem 的成员,相当于(*iter).mem |
iter++ | 令iter 指示容器中的下一个元素 |
iter-- | 令iter 指示容器中的上一个元素 |
iter + n | 令iter 指示原位置向下移动了n 个元素 |
iter - n | 令iter 指示原位置向上移动了n 个元素 |
iter += n | 将iter + n 的结果赋给iter |
iter -= n | 将iter - n 的结果赋给iter |
iter1 - iter2 | 两个指示同一容器的迭代器相减得到所指示元素之间的距离 |
iter1 == iter2 | 判断两个迭代器是否指示同一个元素或相同容器的尾后迭代器 |
iter1 != iter2 | 判断两个迭代器是否不指示同一个元素或相同容器的尾后迭代器 |
>,>=,<,<= | 两个指示同一容器的迭代器,iter1 指示的元素在iter2 指示元素 之后,就说iter1 > iter2 |
2.2 迭代器类型
一般来说,我们无需知道vector
的迭代器的精确类型。想要获得一个指示int
向量的通常做法会是这样:
// to get an iterator of iVec;
vector<int> iVec{1, 2, 3, 4, 5};
// use type auto to declare the iterator;
auto bg = ivec.begin();
int n = 2;
bg += n;
而实际上,在vector
中(所有拥有迭代器的标准库类型都如此)使用iterator
和const_iterator
表示迭代器的类型。const_iterator
类似于我们熟悉的常量指针(顶层const
),能读取但不能修改它所指示的元素值。
- 如果容器是一个常对象,则只能使用
const_iterator
;如果容器不是常对象,则既可以使用iterator
也可以使用const_iterator
,唯一的区别在于前者指示的对象可读可写,而后者指示的对象只可读不可写。 begin()
和end()
运算符返回的具体类型由容器对象是否为常对象决定:如果是常对象,begin()
和end()
返回const_iterator
;否则返回iterator
。
3.迭代器的失效
任何一个可能改变vector
对象容量的操作都会使该vector
对象的迭代器失效。
3.vector
上的一些操作
操作 | 说明 |
---|---|
vector.empty() | 如果vector 为空容器,返回true ,否则返回false |
vector.size() | 返回vector 中的元素个数 |
vector.push_back(t) | 向vector 的尾端(拷贝地)添加一个值为t 的元素,返回指向新添加元素的迭代器 |
vector.emplace_back(args) | 向vector 的尾端(构造地)添加一个由arg 创建的元素,返回指向新添加元素的迭代器 |
vector[n] | 返回vector 中第n 个位置上元素的引用 |
2.标准库类型容器概述
容器(container)是包含对象的数据结构类模板。把数组(array)、队列(queue)、链表(list)、堆栈(stack)、树(tree)、图(tree)等数据结构中的每个节点(都是对象)组织起来,称为一个新的对象。如果抽象了这些数据元素的具体类型,只关心结构的组织,就成为一个类模板。标准模板库(standard template library,STL)提供的标准库类型容器就是这样一个常用数据结构的类模板。
分类
标准库容器分为三大类:
- 顺序容器(sequential_container)
- 关联容器(associative container)
- 容器适配器(container adaptor)
3.顺序容器
1.顺序容器概述
顺序容器是一种元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序容器为程序员提供了控制元素存储顺序和访问顺序的能力,这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
标准容器库中的所有顺序容器都提供了快速顺序访问元素的能力,但是不同的容器在向容器中增删元素或是非顺序访问元素方面略有差异。
要使用不同的顺序容器,只需要包含不同的头文件即可。
容器 | 头文件 | 含义 | 特性 |
---|---|---|---|
vector | <vector> | 可变大小数组 | 支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 |
deque | <deque> | 双端队列 | 支持快速随机访问。在头尾位置插入或删除元素很快。 |
list | <list> | 双向链表 | 只支持双向顺序访问。 在任何位置进行插入/删除操作速度都很快。 |
forward_list | <forward_list> | 单向链表 | 只支持单向顺序访问。在任何位置进行插入/删除操作速度都很快。 |
array | <array> | 固定大小数组 | 支持快速随机访问。不能添加或删除元素。 |
2.顺序容器的选择
除非有很好的理由选择其他容器,否则应当使用
vector
以下是一些选择顺序容器的基本原则:
- 除非你有很好的理由选择其他容器,否则应使用
vector
。 - 如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用
list
或forward_list
。 - 如果程序要求随机访问元素,应使用
vector
或deque
。 - 如果程序只有在读取输入时才需要在容器中间位置插入元素,则
- 首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容易地向
vector
追加数据,然后再调用标准库的sort
函数来重排容器中的元素,从而避免在中间位置添加元素。 - 如果必须在中间位置插入元素,考虑在输入截断使用
list
,一旦输入完成,将list
中的内容拷贝到一个vector
中。
- 首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容易地向
3.顺序容器的使用(一)
4.顺序容器的操作
把所有顺序容器支持的操作列表如下:
vector | deque | list | forward_list | array | ||
---|---|---|---|---|---|---|
构造函数 | Container con | √ | √ | √ | √ | √ |
Container con1(con2) | √ | √ | √ | √ | √ | |
Container con(beg,end) | √ | √ | √ | √ | × | |
Container con{a,b,c...} | √ | √ | √ | √ | √ | |
赋值与swap | con1 = con2 | √ | √ | √ | √ | √ |
con = {a,b,c...} | √ | √ | √ | √ | × | |
con1.swap(con2) | √ | √ | √ | √ | √ | |
swap(con1,con2) | √ | √ | √ | √ | √ | |
con.assign(beg,end) | √ | √ | √ | √ | × | |
con.assign(initial_list) | √ | √ | √ | √ | × | |
con.assign(n,t) | √ | √ | √ | √ | × | |
大小 | ` |