C++第二部分:C++标准库
第九章:顺序容器
9.1 顺序容器概述
顺序容器类型
- vector:可变大小数组,支持快速随机访问,在尾部之外的位置插入或插入或删除元素可能很慢
- deque:双端队列,支持快速随机访问,在头尾位置插入/删除速度很快
- list:双向链表,只支持双向顺序访问,在list中任何位置进行插入/删除操作速度都很快
- forward_list:单向链表,只支持单向顺序访问,在链表任何位置进行插入/删除操作速度都很快
- array:固定大小数组,支持快速随机访问,不能天剑或删除元素
- string:与vector相似的容器,但专门用于保存字符。随机访问快,在尾部插入/删除速度快
forward_list没有size操作。
9.2 容器库概览
一般来说,每个容器都定义在一个与类型名相同的头文件中,容器均定义为模板类。
**对容器可以保存的元素类型的限制:**如没有默认构造函数的对象。
容器操作:
类型别名:
- iterator:此容器类型的迭代器类型
- const_iterator:可以读取元素,但不鞥修改元素的迭代器类型
- size_type:无符号整型类型,足够保存此种容器类型最大可能容器的大小
- difference_type:有符号整型类型,足够保存两个迭代器之间的距离
- value_type:元素类型
- reference:元素的左值类型,与value_type&的含义相同
- const_reference:元素的const左值类型
构造函数:
- C c; 默认构造函数
- C c1(c2); 构造c2的拷贝c1
- C c(b, e); 构造c,将迭代器b和e指定的范围内的元素拷贝到c(array不支持)
- C c(a, b, c…); 列表初始化c
赋值与swap:
- c1 = c2:将c1中的元素替换为c2中元素
- c1 = {a, b, c…}:将c1中的元素替换为列表中的元素(不适用于array)
- s.swap(b):交换a和b的元素
- swap(a, b):与a.swap(b)等价
大小:
- c.size():c中元素的数目(不支持forward_list)
- c.max_size():c可保存的最大元素数目
- c.empty():若c中存储了元素,返回false,否则返回true
添加/删除元素(不适用于array):
- c.insert(args):将args中的元素拷贝进c
- c.emplace(inits):使用inits构造c中一个元素
- c.erase(args):删除args指定的元素
- c.clear()删除c中的所有元素,返回void
关系运算符:
- ==,!=:所有容器都支持相等(不等)运算符
- <,<=,>,>=:关系运算符(无序关联容器不支持)
获取迭代器:
- c.begin(),c.end():返回指向c的首元素和尾元素之后位置的迭代器
- c.cbegin(),c.cend():返回const_iterator
反向容器的额外成员(不支持forward_list):
- reverse_iterator:按逆序寻址元素的迭代器
- const_reverse_iterator:不能修改元素的逆序迭代器
- c.rbegin(),c.rend():返回指向c的尾元素和首元素之前位置的迭代器
- c.crbegin(),c.crend():返回const_reverse_iterator
9.2.1 迭代器
**迭代器运算符:***iter,iter->name,++iter,–iter,iter1 == iter2,iter1 != iter2
**迭代器算术运算:**iter + n,iter - n,iter1 += n,iter -= n,iter1 -iter2,>,>=,<,<=
forward_list迭代器不支持递减运算符
这些迭代器算术运算只能应用于string、vector、deque和array
9.2.3 begin和end成员
不以c开头的函数都是被重载过的,对一个非常量对象调用这些成员时,将得到的是返回iterator版本,只有在对一个const对象调用这些函数时,才会得到一个const版本。
9.2.4 容器定义和初始化
只有顺序容器(不包括array)的构造函数才接受大小参数。
- C seg(n):seq包含n个元素,这些元素进行了值初始化。
- C seg(n, t):seq包含n个初始化为t的元素。
当将一个容器初始化为另一个容器的拷贝时,两个容器类型和元素类型都必须相同。当传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的了,同时新容器和原容器的元素类型也可以不同,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。
**标准库array具有固定大小:**标准库array的大小也是类型的一部分,如array<int, 42>;
9.2.5 赋值和swap
容器赋值运算:
- c1=c2:将c1中的元素替换为c2中元素的拷贝。c1和c2必须具有相同的类型
- c={a, b, c…}:将c1中元素替换为初始化列表中元素的拷贝
- swap(c1, c2)、c1.swap(c2):交换c1和c2中的元素,c1和c2必须具有相同的类型,swap通常比从c2向c1拷贝的元素多
- assign操作不适用于关联容器和array。
- seq.assign(b, e):将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素
- seq.assign(i1):将seq中的元素替换为初始化列表i1中的元素
- seq.assign(n, t):将seq中的元素替换为n个置位t的元素
赋值相关运算符会导致指向左边容器内部的迭代器、引用和指针失效。而swap操作将容器内容交换不会导致指向容器的迭代器、引用和指针失效。容器类型为array和string的情况除外。
assign允许我们从一个不同但相容的类型赋值。
swap只是交换了两个容器的数据结构。
9.2.6 容器大小操作
每个容器都有返回容器中元素数目的size成员函数、当size为0时返回true的empty、返回一个大于或等该类型容器所能容纳的最大元素的值的max_size。除了forward_list支持max_size和empty。
9.2.7 关系运算符
每个容器类型都支持相等运算符==和!=,除无序关联容器外的所有容器都支持关系运算符>、>=、<、<=,关系运算符左右两边的运算对象必须是相同类型的容器。
9.3 顺序容器操作
9.3.1 向容器添加元素
这些操作会改变容器的大小,array不支持这些操作。
forward_list有自己专有版本的insert和emplace,forward_list不支持push_back和emplace,vector不支持push_front和emplace_front.
- c.push_back(t)、c.emplace_back(args):在c的尾部创建一个值为t或由args创建的元素,返回void。
- c.push_front(t)、c.emplace_front(args):在c的头部创建一个值为t或由args创建的元素,返回void。
- c.insert(p, t)、c.emplace(p, args):在迭代器p指向的元素之前创建一个值为t或由args创建的元素,返回指向新添加的元素的迭代器。
- c.insert(p, n, t):在迭代器p指向的元素之前插入n个值为t的元素。返回指向新添加的第一个元素的迭代器,若n为0,则返回p。
- c.insert(p, b, e):在迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素,返回指向新添加的第一个元素的迭代器,若范围为0,则返回p。
- c.insert(p, i1):i1是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前,返回指向新添加的第一个元素的迭代器,若列表为空,则返回p。
向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效。
当我们用一个对象来初始化容器时,或将一个对象插入到容器中时。实际上放入到容器中的是对象值得另一个拷贝,而不是对象本身。
9.3.2 访问元素
at和下标操作只适用于string、vector、deque和array。
back不适用于forward_list。
- c.back():返回c中尾元素的引用。若c为空,函数行为未定义。
- c.front():返回c中首元素的引用,若c为空,函数行为未定义。
- c[n]:返回c中下标为n的元素的引用,n是一个无符号整数,若n>=c.size(),则函数行为未定义
- c.at(n):返回下标为n的元素的运用,如果下标越界,则抛出一out_of_range异常
9.3.3 删除元素
这些操作会该表容器的大小,所以不适用于array。
forward_list有特殊版本的erase,
forward_list不支持pop_back;vector和string不支持pop_front。
- c.pop_back():删除c中尾元素,若c为空,则函数行为未定义吗,函数返回void
- c.pop_front():删除c中首元素,若c为空,则函数行为未定义吗,函数返回void
- c.erase§:删除迭代器p所指向的元素,返回一个指向被删元素之后元素的迭代器,若p指向尾元素,则返回尾后迭代器,若p是尾后迭代器,则函数行为未定义。
- c.erase(b,e):删除迭代器b和e所指向范围内的元素,返回一个指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则函数也返回尾后迭代器。
- c.clear():删除c中所有元素,返回void
删除deque中除首尾位置之外的任何元素都会是所有迭代器、引用和指针失效,指向vector或string中删除点之后位置的迭代器,引用和指针都会失效。
9.3.4 特殊的forward_list操作
在forward_list中插入或删除元素的操作
- lst.before()、lst.cbefore_begin():返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。cbefore_begin()返回一个const_iterator
- lst.insert_after(p, t)、lst.insert_after(p, n, t)、lst.insert_after(p, b, e)、lst.insert_after(p, i1):在迭代器p之前的位置插入元素。t是一个对象,n是数量,b和e是表示范围的一对迭代器(b和e不能指向lst内),i1是一个花括号列表,返回一个指向最后一个插入元素的迭代器,如果范围为空,则返回p。若p尾尾后迭代器,则函数行为未定义。
- emplace_after(p, args):使用args在p制定的位置之后创建一个元素。返回一个指向这个元素的迭代器,若p为尾后迭代器,则函数行为未定义
- lst.earse_after§、lst.erase_after§:删除p指向的位置之后的元素,或删除从b之后到(但不包含)e之间的元素。返回一个指向被删元素之后元素的迭代器,若不存在这样的元素,则返回尾后迭代器。如果p指向lst的尾元素或者是一个尾后迭代器,则函数发生未定义行为。
9.3.5 改变容器大小
resize不适用于array。
- c.resize(n):调整c的大小为n个元素,若n<c.size(),则多出来的元素被丢弃。若必须添加新元素,对元素进行值初始化
- c.resize(n, t):调整c的大小为n个元素,任何新添加的元素都初始化为值t
如果resize缩小容器,则指向被删除元素的迭代器、引用和指针都会失效;对vector。string或deque进行resize可能导致迭代器,指针和引用失效。
9.4 vector对象是如何增长的
管理容量的成员函数
shrink_to_fit只适用于vector、string和deque。
capacity和reserve只适用于vector和string。
- c.shrink_to_fit():请将capacity()减少为与size()相同大小
- c.capacity():不重新分配内存空间的话,c可以保存多少元素
- c.reserve(n):分配至少能容纳n个元素的内存空间
9.5 额外的string操作
** 9.5.1 构造string的其他方法**
n、len2和pos2都是无符号值
- string s(cp, n):s是cp指向的数组中前n个字符的拷贝,此数组至少应该包含n个字符
- string s(s2, pos2):s是string s2从下标pos2开始的字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。
- string s(s2, pos2, len2):s是string s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()-pos2个字符
如果位置pos2大于s2的大小,则构造函数抛出一个out_of_range异常。
substr操作
- s.substr(pos, n):返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0。n的默认值为s.size()-pos,即拷贝从pos开始的所有字符。如果开始位置超过了string的大小,则substr函数抛出一个out_of_rang异常。
9.5.2 改变string的其他方法
- s.insert(pos, args):在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用;接受迭代器的版本返回指向第一个插入字符的迭代器
- s.erase(pos, len):删除从位置pos开始的len个字符,如果len被忽略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用
- s.assign(args):将s中的字符替换为args指定的字符。返回一个指向s的引用
- s.append(args):将args追加到s。返回一个指向s的引用
- s.replace(range, args):删除s中范围range内的字符,替换为args指定的字符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用。
args可以是下列形式之一;append和assign可以使用所有形式。
str不能与s相同,迭代器b和e不能指向s。
str:字符串str
str.pos, len:str中从pos开始最多len个字
cp.len:从cp指向的字符数组的前(最多)len个字符
cp:cp指向的以空字符结尾的字符数组
n, c:n个字符c
b, e:迭代器b和e指定的范围内的字符
初始化列表:花括号包围的,以逗号分隔的字符列表
replace和insert所允许的args形式依赖于range和pos是如何指定的
replace(pos, len, args) | replace(b, e, args) | insert(pos, args) | insert(iter, args) | args可以是 |
---|---|---|---|---|
是 | 是 | 是 | 否 | str |
是 | 否 | 是 | 否 | str, poe, len |
是 | 是 | 是 | 否 | cp, len |
是 | 是 | 否 | 否 | cp |
是 | 是 | 是 | 是 | n, c |
否 | 是 | 否 | 是 | b2, e2 |
否 | 是 | 否 | 是 | 初始化别表 |
9.5.3 string搜索操作
搜索操作返回指定字符出现的下标,如果未找到则返回npos。
- s.find(args):查找s中args第一次出现的位置
- s.rfind(args):查找s中args最后一次出现的位置
- s.find_first_of(args):在s中查找args中任何一个字符第一次出现的位置
- s.find_last_of(args):在s中查找args中任何一个字符最后一次出现的位置
- s.find_first_not_of(args):在s中查找第一个不在args中的字符
- s.find_last_not_of(args):在s中查找最后一个不在args中的字符
args必须是以下形式之一
- c, pos:从s中位置pos开始查找字符c。pos默认为0
- s2, pos:从s中位置pos开始查找字符串s2。pos默认为0
- cp, pos:从s中位置pos开始查找指针cp指向的以空字符结尾的c风格字符串。pos默认为0
- cp, pos, n:从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n无默认值
9.5.4 compare函数
s.compare的几种参数形式
- s2:比较s和s2
- pos1, n1, s2:将s中从pos1开始的n1个字符与s2进行比较
- pos1, n1, s2, pos2, n2:将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较
- cp:比较s与cp指向的以空字符结尾的字符数组
- pos1, n1, cp:将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较
- pos1, n1, cp, n2:将s中从pos1开始的n1个字符与cp指向的地址开始的n2个字符进行比较
9.5.5 数值转换
string和数值之间的转换
- to_string(val):一组重载函数,返回数组val的string表示。val可以是任何算术类型。对每个浮点类型和int或更大的整数,都有相应版本的to_string。与往常一样,小整型会被提升
- stoi(s, p, b)、stol(s, p, b)、stoul(s, p, b)、stoll(s, p, b)、stoull(s, p, b):返回s的起始子串(表示整数的内容)的数值,返回值类型分别是int,long,unsigned long,long long,unsigned long long,b表示转换所用的基数。默认为10。p是size_t指针,用来保存s中第一个非数值字符的下标,p默认为0,即,函数不保存下标
- stof(s, p)、stood(s, p)、stold(s, p):返回s的起始子串(表示浮点数内容)的数值,返回值类型分别是float,double或long double。参数p的作用与整数转换函数中一样
如果string不能转换为一个数值,这些函数抛出一个invalid_argument异常。如果转换得到的数值无法用任何类型来表示,则抛出一个out_of_range异常。
9.6 容器适配器
标准库定义了三个顺序容器的适配器:stack、queue和priority_queue。
所有容器适配器都支持的操作和类型
- size_type:一种类型,足以保存当前
- value_type:元素类型
- container_type:实现适配器的底层容器类型
- A a;创建一个名为a的空适配器
- A a©;创建一个名为s的适配器,带有容器c的一个拷贝
- 关系运算符:每个适配器都支持所有关系运算符:==,!=,<,<=,>和>=这些运算符返回底层容器的比较结果
- a.empty():若a包含任何元素,返回false,否则返回true
- a.size():返回a中的元素数目
- swap(a, b)、a.swap(b):交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同。
如嘉定dep是一个deque我们可以stack stk(dep)来定义。
默认情况下,stack和queue是基于deque实现的,priority_queue实在vector之上实现的。我们可以在创建一个适配器时讲一个命名的顺序容器作为第二个类型参数,来重载默认容器类型。
所有适配器都要求容器具有添加、删除和访问尾元素的的能力,因此适配器不能构造在array和forward_list之上。
stack只要求push_back、pop_back和back操作,因此可以使用除array和forward_list之外的任何容器类型来构造stack。
queue适配器要求back、push_back、front和push_front,因此他可以构造于list或deque之上,但不能基于vector构造。
proiority_queue要求front、push_back、pop_back和随机访问能力因此他可以构造于vector或deque之上,但不能基于list构造。
栈适配器其他操作
stack定义在stack头文件中
栈默认基于deque实现,也可以在list或vector之上实现
- s.pop():删除栈顶元素,但不返回该元素值
- s.push(item)、s.emplace(args):创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,或者由args构造。
- s.top():返回栈顶元素,但不将元素弹出栈
队列适配器
queue和priority_queue适配器定义在queue头文件中
其他的queue和priority_queue操作
queue默认基于deque实现,也可以用list或vector实现
priority_queue默认基于vector实现,也可以用deque实现
- q.pop():返回queue的首元素或priority_queue的最高优先级的元素,但不删除此元素
- q.front()、q,back()(只适用于queue):返回首元素或尾元素,但不删除此元素
- q.top():返回最高优先级元素,但不删除该元素
- q.push(item)、q.emplace(args):在queue末尾或priority_queue中恰当的位置创建一个元素,其值为item,或者由args构造