1、顺序容器概述
以下表格列出了标准库中的顺序容器,所有的顺序容器都提供了快速顺序访问元素的能力。
vector | 可变大小数组。支持快速随机访问。元素是连续存储。 |
deque | 双端队列。支持快速随机访问。在头尾位置插入、删除速度很快。 |
list | 双向链表。支持双向顺序访问。在任何位置插入、删除速度都很快。 |
forward_list | 单向链表。只支持单向顺序访问。在任何位置插入、删除速度都很快。 |
array | 固定大小数组。支持快速随机访问。不能添加或删除元素。 |
string | 与vector类似的容器,但专门用于保护字符。随机访问快,在尾部插入或删除速度快。 |
forward_list和array是新的C++标准增加的类型。
确定使用哪种顺序容器
通常,使用vector是最好的选择,除非有很好的理由选择其他容器。
以下是一些选择容器的原则:
- 除非你有很好的理由选择其他容器,否则应使用vector
- 如果你的程序有很多小的元素,且额外开销很重要,则不要使用list和forward_list
- 如果程序要求随机访问元素,应使用vector或deque
- 如果要求在容器的中间插入或删除元素,应使用list或forward_list
- 如果需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque
2、容器库概述
每个容器都定义在一个头文件中,文件名和类型名相同。即,deque定义在头文件deque中,list定义在头文件list中,等等。
顺序容器几乎可以保存任意类型的元素。特殊情况下是,定义一个容器,其元素类型是另外一个容器。如:vector<vector<string>> lines;
容器操作:
类型别名 | |
iterator | 此容器类型的迭代器类型 |
const_iterator | 可以读取元素,但是不能修改元素的迭代器类型 |
size_type | 无符号整数类型,保存此种容器类型最大可能容器的大小 |
difference_type | 带符号整数类型,足够保存两个迭代器之间的距离 |
value_type | 元素类型 |
reference | 元素的左值类型,与value_type & 含义相同 |
const_reference | 元素的const左值类型(即,cosnt value_type &) |
迭代器运算
iter+n | 迭代器指向的位置向前移动n个位置 |
iter-n | 迭代器指向的位置向后移动n个位置 |
iter += n | iter = iter + n |
iter -= n | iter = iter - n |
iter1- iter2 | |
>,>=,<,<= | iter1如果在iter2指向的位置之前,则iter1小于iter2 |
迭代器范围
[ bigen, end)
while(begin != end)
{
*begin = val;//将begin指向位置的元素值设为val
++begin;//移动迭代器,获取下一个位置
}
迭代器的类型
- auto len = line.size();//len的类型是string::size_type;
- vector<int>::iterator it;//it能读写vector<int>的元素。
- vector<int>::const_iterator it2;//it2只能读元素,不能写元素。
- 方向容器的成员,reverse_iterator和const_reverse_iterator,它们和之前的那两个的++,--含义是相反的,其它含义都是一样的。
begin和end成员
list<string>a = {"ya","xi","ku"};
auto it1 = a.begin();//list<string>::iterator
auto it2 = a.rbegin();//list<string>::reverse_iterator
auto it3 = a.cbegin();//list<string>::const_iterator
auto it4 = a.crbegin();//list<string>::const_reverse_iterator
与顺序容器大小相关的构造函数
vector<int> ivec(10, -1);//10个int元素,每个值都初始化为1
list<string> svec(10, "hello");//10个string元素,每个值初始化为hello
forward_list<int> ivec(10);//10个int元素,每个都初始化为0
deque<string> svec(10);//10个string元素,每个都是空字符串
列表初始化(C++11标准)
list<string> peo = {"ya", "xi", "kui"};
vector<char *> peo = {"ya", "xi", "kui"};
标准库array具有固定的大小
与内置数组一样,标准库array的大小也是类型的一部分。当定义一个array时,除了指定元素类型,还要指定容器大小。
array<int, 42> //这是一个数据类型,保存42个int的数组
array<string, 10> //这是一个数据类型,保存10个string的数组
array<int, 10> ival1;//10个默认初始化的int
array<int, 10> ival2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//列表初始化
array<int, 10> ival3 = {42};//ival[0] = 42,其余元素都是0
赋值和swap
c1 = c2 | 将c1中的元素变为c2中元素的拷贝。c1,c2必须有相同的类型 |
c = {a,b,c....} | 将c中的元素变为初始化列表中元素的拷贝,array除外。 |
swap(c1, c2) | 交换c1和c2的元素。两个具有相同的类型。速度比从c2向c1拷贝元素快得多。 |
c1.swap(c2) | 交换c1和c2的元素。两个具有相同的类型。速度比从c2向c1拷贝元素快得多。等价于swap(c1, c2) |
seq.assign(b,e) | 将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素。 |
seq.assign(i1) | 将seq中的元素替换为初始化列表i1中的元素 |
seq.assign(n, t) | 将seq中的元素替换为n个值为t的元素 |
备注:
- assign操作不适用于关联容器和array
- 赋值相关运算会导致指向左边容器内部的迭代器,引用和指针失效。
- swap操作将容器内容交换不会导致指向容器的迭代器,引用和指针失效。(容器类型为array和string的情况除外)
使用assign
赋值运算符要求左边和右边的运算对象具有相同的类型。但是assign允许我们从一个不同但是相容的类型赋值,或者从一个容器的一个子序列赋值。
list<string> names;
vector<const char *> oldstyle;
names = oldstyle;//错误,容器类型不匹配
names.assign(oldstyle.cbegin(), oldstyle.cend());
//对assign的调用将names中的元素替换为迭代器指定的范围中的元素的拷贝。
第二个版本的assign,接受一个整型值和一个元素值
<span style="white-space:pre"> </span>list<string> slist(1);//1个元素,为空string
slist.assign(10, "Hiya");//10个元素,每个都是“Hiya”
使用swap
vector<string> svec1(10);
vector<string> svec2(24);
swap(svec1,svec2);
//调用swap后,svec1将包含24个string元素,svec2将包含10个string元素。
- 除了string之外,指向容器的迭代器,引用和指针在swap之后都不会失效。它们仍然指向swap之前所指向的那些元素。但是这些元素已经属于是不同的容器了。
- swap两个array会真正交换它们的元素。在swap操作之后,指针、引用和迭代器所绑定的元素保持不变,但元素值已经与另一个array中对应元素的值进行了交换。
容器大小操作
- 成员函数size返回容器中元素的数目
- empty当size为0时返回布尔值true,否则返回false。
- max_size返回一个大于或等于该类型容器所能容纳的最大元素数的值。
- forward_list支持max_size和empty,但是不支持size。
关系运算符
==, !=, >, >=, <, <=
关系运算符左右两边的运算对象必须是相同类型的容器,且必须保存相同类型的元素。