[C++ Primer Note8] 顺序容器

所谓的顺序容器即元素在顺序容器中的顺序与其加入容器时的位置相对应。标准库还定义了几种关联容器,关联容器中元素的位置由元素相关联的关键字值决定。所有的容器类都共享公共的接口,不同容器按不同方式对其进行扩展。

  1. 标准库中的顺序容器常用的有以下这些:
  • vector:可变大小数组,支持快速随机访问。非尾部插入删除很慢
  • deque:双端队列,支持快速随机访问,在头尾插入/删除速度很快
  • list:双向链表,只支持双向顺序访问,任何位置插入/删除都很快
  • forward_list:单向链表,只支持单向顺序访问,任何位置插入/删除都很快
  • array:固定大小数组,支持快速随机访问,不能添加或删除元素
  • string:与vector相似的容器,但专门用于保存字符
  1. 现代C++程序应该使用标准库容器,而不是更原始的数据结构,如内置数组。一般来说,使用vector是最好的选择,除非你有很好的理由选择其他容器。
  2. 容器类型上的操作形成了一种层次:
  • 某些操作是所有容器类型都提供的
  • 另外一些操作仅针对顺序容器关联容器无序容器
  1. 一般来说,每个容器都定义在一个头文件中,文件名与类型名相同。容器均定义为模板类,我们必须提供额外信息来生成特定的容器类型。
  2. 顺序容器几乎可以保存任意类型的元素,但某些容器操作对元素类型有其自己的特殊要求。
  3. 常用容器操作(通用)
    容器操作

    反向容器额外操作
  4. 一个迭代器范围(iterator range)由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者尾后位置。这两个迭代器通常被称为begin和end。这种元素范围被称为左闭合区间,其标准数学描述为:

[begin, end)
begin和end必须指向相同的容器,end可以和begin指向同样的位置,但不能指向begin之前的位置。

  1. 之所以规定左闭合范围是因为我们可以安全方便地处理元素,如下:
while(begin!=end){
  *begin=val;
  ++begin;
}
  1. 通过容器内置的类型别名,我们可以在不了解容器中元素类型的情况下使用它(通过value_type)。如果需要元素类型的一个引用,可以使用referenceconst_reference
  2. 当auto与begin或end结合使用时,获得的迭代器版本依赖于容器类型(因为实际上begin被重载过),但以c开头的版本一定会获得const_iterator。当不需要写访问时,应该使用cbegin和cend。
  3. 每个容器类型都定义了一个默认构造函数。除array之外,其他容器的默认构造函数都会创建一个指定类型的空容器。
  4. 为了创建一个容器为另一个容器的拷贝,两个容器的类型及元素类型必须匹配。不过如果传递迭代器参数来拷贝一个范围,就不要求容器类型是相同的了。
  5. 新标准中,我们可以对一个容器进行列表初始化,这样我们显式指定了每个元素的值,还隐含了容器的大小。
  6. 与内置数组一样,标准库array的大小也是类型的一部分。定义一个array时,除了指定元素类型,还要指定容器大小:
  array<int,42>;
  array<string,10>;
  1. 由于大小是array的一部分,array不支持普通的容器构造函数,这些构造函数或显式或隐式地确定了容器的大小。
  2. 与其他容器不同,一个默认构造的array是非空的:它包含与其大小一样多的元素,这些元素都被默认初始化,就像一个内置数组一样。如果我们对array进行列表初始化,初始值数目必须等于或小于array的大小,如果小于,则剩余靠后的元素都会进行值初始化。这种情况下,如果元素是类类型,则必须要有一个默认构造函数。
  3. 值得注意的是,虽然我们不能对内置数组进行拷贝或对象赋值操作,但array并无此限制。array要求容器类型,元素类型和大小都一样,因为大小也是array类型的一部分
  4. 赋值运算符将其左边容器中的全部元素替换成右边容器中元素的拷贝,要求左边和右边具有相同的类型
  5. 顺序容器(除了array)还定义了一个名为assign的成员,允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。
  6. swap操作交换两个相同类型容器的内容,除了array外,swap只是交换了两个容器的内部数据结构。所以迭代器,引用和指针仍指向之前所指的元素,但是这些元素已经去到另外一个容器了。swap两个array则会真正交换它们的元素
  7. 新标准库中,容器既提供成员函数版本的swap,也提供非成员版本的swap,由于非成员版本的swap在泛型编程中很重要,所以统一使用非成员版本的swap是好习惯。
  8. 每个容器类型都支持相等运算符;除了无序关联容器外所有容器都支持关系运算符,关系运算符左右两边的运算对象必须是相同类型的容器,且必须保存相同类型的元素。比较两个容器实际上是元素的逐对比较,与string的字典序比较类似。
  9. 如果元素类型不支持所需的运算符(比如==和>),那么保存这种元素的容器就不能使用相应的关系运算。
  10. 容器提供的操作与它组织元素的方式有密切关系(实际上就是内部数据结构),也与它的语义有关(比如array不支持改变大小的操作)
  11. 向顺序容器添加元素:
    向顺序容器添加元素
  12. 访问顺序容器的元素:
    访问元素
  13. 删除顺序容器的元素:
    删除元素
  14. forward_list本质上一个单向链表,删除或添加某个元素会影响前驱节点的后继,但是单向链表并没有简单的方法来获取一个元素的前驱。所以一个forward_list中添加或删除元素的操作时通过改变给定元素之后的元素来完成的。所以从forward_list添加或删除元素的时候,我们往往关注两个迭代器,如下:
forward_list<int> flst={0,1,2,3,4,5,6,7,8,9};
auto prev = flst.before_begin();
auto curr = flst.begin();
while(curr!=flst.end()){
  if(*curr%2)
    curr=flst.erase_after(prev);
  else{
    prev=curr++;
  }
}
在forward_list中插入或删除元素
  1. 在容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针,引用或迭代器失效,仍旧使用失效的它们是一种严重的程序设计错误。具体的失效情况可以通过分析容器的底层数据结构并将迭代器假想为指针大致得出,此处不再赘述。特别是如果在使用迭代器的循环中具有添加/删除的操作,一定要注意迭代器的更新,保证其正确性
  2. vectorstring不得不获取新的内存空间时,通常会分配比新的空间需求更大的内存空间作为备用。但vector只有迫不得已的时候才分配新的内存空间
  3. vectorstring提供了一些成员函数,允许我们与它的实现中内存分配部分互动:
    容器大小管理操作
  4. 除了顺序容器共同的操作之外,string类型还提供了一些额外的操作。这些操作中大部分要么是提供了string类和C风格字符数组之间的相互转换,要么是增加了允许我们用下标代替迭代器的版本。
  5. string类型提供了非常多的成员函数,总的来说,包括多种构造函数返回子串函数,多种修改函数,多种搜索字符函数,多种比较函数,以及多种string和数值之间的转换函数,此处不一一赘述,需要时再查阅资料即可。
  6. 除了顺序容器外,标准库还定义了三个顺序容器适配器:stackqueuepriority_queue适配器(adaptor)是标准库的一个通用概念。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。例如,stack适配器接受一个顺序容器,使其操作看起来像一个stack一样。
    所有容器适配器都支持的操作和类型
  7. 默认情况下,stack和queue是基于deque实现的,priority_queue是在vector之上实现的。我们可以在创建一个适配器时将一个命名的顺序容器作为第二个类型参数,来重载默认容器类型。
//在vector上实现的空栈
stack<string,vector<string>> str_stk;

对于给定的适配器,可以使用哪些容器是有限制的,具体的规则可以结合适配器以及容器的接口来分析,要求适配器接口一定能通过容器的接口直接或简单间接实现,就不一一赘述了。

栈的接口

队列和优先队列接口(1)

队列和优先队列接口(2)

值得注意的是:这里关于pop的用法写错了,pop返回的是void,这是因为如果pop要弹出元素,则必须返回元素的值而不是引用。然而接收值的时候有可能发生异常(比如通过这个值来初始化一个类类型),这时丢失的元素就再也找不回来了。所以往往pop和top搭配使用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,514评论 1 51
  • #1.顺序容器概述 #2.容器库概览迭代器容器类型成员begin和end成员容器定义和初始化赋值和swap容器大小...
    MrDecoder阅读 1,220评论 0 1
  • 2. C++标准库 2.1 IO库 IO对象无拷贝或赋值,进行IO操作的函数通常以引用方式传递和返回流。 IO库条...
    王侦阅读 1,310评论 0 0
  • 今天听了刘老师的课,收获真是太多了。我一直对老公有情绪,老公对我也有情绪,听了今天的课,我才知道是怎么回事。在...
    伟_e56f阅读 219评论 0 0
  • 始于心动,终于白首,拥之则安,伴之则暖! 好想一起结伴赏雪,银装素裹的西岭湖一定特别美吧!
    VitoLiufu阅读 238评论 0 0