写在前面:本文是阅读《C++标准库(第二版)》的读书笔记。

文章目录

  • 6.1 STL组件(Component)
  • 6.2 容器(Container)
    • 6.2.1 序列式容器(Sequence container)
      • vector
      • Deque
      • Array
      • List
    • 6.2.2 关联式容器(Associative container)
      • Set 和Multiset 实例
      • Map和Multimap 实例
    • 6.2.3 无序容器(Unordered container)
      • unordered set/multiset 实例
      • unordered map/multimap实例

6.1 STL组件(Component)

若干组件精心合作,构筑起STL的基础。 这些组件中最关键的是容器、迭代器和算法。

  • 容器(Container),用来管理某类对象的集合。每一种容器都有其优点和缺点,所以,为了应付不同的需求,STL准备了不同的容器类型。容器可以是array或linked list,或者每个元素有一个特别的key。

  • 迭代器(Iterator),用来在一个对象集合内遍历元素。这个对象集合或许是一个容器,或许是容器的一部分。迭代器的主要好处是,为所有各式各样的容器提供了一组很小的共通接口。例如其中一个操作是行进至集合内的下一个元素。至于如何做到当然取决于集合的内部结构。不论这个集合是array或tree或hash table,此一行进动作都能成功,因为每一种容器都提供了自己的迭代器,而这些迭代器了解容器的内部结构,知道该做些什么。

    迭代器的接口和寻常的pointer差不多,调用operator++就累进,调用operator * 就访问被指向的值。所以你可以把迭代器视为一种 smart pointer,能够把“前进到下一元素”的意图转换为合适的操作。

  • 算法(Algorithm),用来处理集合内的元素。它们可以出于不同目的而查找、排序、修改、使用元素。通过迭代器的协助,我们只需要撰写一次算法,就可以将它应用到任意容器,因为所有容器的迭代器都提供一致的接口。
    你还可以提供一些特殊的辅助函数供算法调用,从而获取更佳的灵活性,这样你就可以一方面运用标准算法,一方面配合自己特殊或复杂的需求。例如,你可以提供自己的查找准则(search criterion)或元素合并时的特殊操作。特别是C++11新引入了lambda,你得以轻松地指明在容器元素上进行任何种类的操作。

STL的基本观念就是将数据和操作分离。数据由容器类加以管理,操作则由可定制的算法定义之。 迭代器在两者之间充当粘合剂,使得任何算法都可以和任何容器交互作用。

STL的一个根本特性是,所有组件都可以针对任意类型运作。顾名思义,所谓的standard template library 意味着其内的所有组件都是“可接受任意类型”的template,前提是这些类型必须能够执行必要的操作。因此STL成为了泛型编程概念下的一个出色范例。容器和算法被泛化为可适用于任意type和class。

6.2 容器(Container)

容器用来管理一大群元素。 为了适应不同需求,STL提供了不同的容器。

总的来说,容器可分为三大类:

  1. 序列式容器(Sequence container),这是一种有序(ordered)集合,其内每个元素均有确凿的位置------取决于插入时机和地点,与元素值无关。如果你以追加方式对一个集合放入6个元素,它们的排列次序将和置入次序一致。STL提供了5个定义好的序列式容器:array,vector,deque,list和forward_list.
  2. 关联式容器(Associative container),这是一种已排序(sorted)集合,元素位置取决于其value(或key—如果元素是个key/value pair)和给定的某个排序准则。如果将6个元素置入这样的集合中,它们的值将决定它们的次序,和插入次序无关。STL提供了4个关联式容器:set,multiset,map和multimap.
  3. 无序容器(Unordered container),这是一种无序集合(unordered collection),其内每个元素的位置无关紧要,唯一重要的是某特定元素是否位于此集合内。元素值或者安插顺序,都不影响元素的位置,而且元素的位置有可能在容器生命中被改变。STL内含4个预定义的无序容器:unordered_set,unordered_multiset,unordered_map,unordered_multimap.

6.2.1 序列式容器(Sequence container)

STL内部预先定义好了以下序列式容器:

  • Array(其class 名为array)
  • Vector
  • Deque
  • List(singly/doubly linked)

vector

vector将其元素放在一个dynamic array 中管理。 它允许随机访问,也就是说,你可以利用索引直接访问任何一个元素。在array尾部附加元素或移除元素都很快速,但是在array内部安插元素就比较费时。

以下例子针对整数类型定义了一个vector,插入6个元素,然后打印所有元素:

#include<iostream>
#include<vector>
using namespace std;int main(){vector<int> coll; //vector container for integer elements//append elements with values 1 to 6for(int i=1;i<=6;i++)coll.push_back(i);//print all elements followed by a spacefor(int i=0;i<coll.size();i++)cout<<coll[i]<<" ";cout<<endl;
}

Deque

所谓deque,是“double-ended queue”的缩写。它是一个dynamic array,可以向两端发展,因此不论在尾部或头部安插元素都十分迅速。在中间部分安插元素则比较费时,因此必须移动其他元素。

以下例子声明了一个元素为浮点数的deque,并在容器头部安插1.1 到6.6 供6个元素,最后打印出来所有元素。

#include<iostream>
#include<deque>
using namespace std;int main(){deque<float> coll;//insert elements form 1.1 to 6.6 each at the frontfor(int i=1;i<=6;i++)coll.push_front(i*1.1);  //insert at the front//print all elements followed by a spacefor(int i=0;i<coll.size();i++)cout<<coll[i]<<" ";cout<<endl;
}

本例中,下面这一行将deque的头文件包含进来:

#include<deque>

下面的声明则是产生一个空的浮点数集合:

deque<float> coll;

push_front()函数用来安插元素:

coll.push_front(i*1.1);

push_front()会将元素安插在集合前端。注意,这种安插方式造成的结果是,元素排放次序于安插次序恰好相反,因为每个元素都安插在上一个元素的前面。程序输出结果如下:

6.6 5.5 4.4 3.3 2.2 1.1

你也可以使用成员函数push_back()在deque尾部附加元素。vector并未提供push_front(),因为其时间效率不佳(在vector头部安插一个元素,需要先移动全部元素)。一般而言,STL容器只提供具备良好时间效率的成员函数,所谓“良好”通常意味着其复杂度为常数或者对数,以免程序员调用性能很差的函数。

Array

一个array对象是在某个固定大小的array(有时称为一个static array 或C array)内管理元素。因此,你不可以改变元素个数,只能改变元素值。 必须在建立时就指明其大小。 array允许随机访问,意思是可以直接访问任何一个元素,只要指定相应的索引。

下面定义了一个array,元素是string

#include<bits/stdc++.h>
using namespace std;int main(){//array container of 5 string elementsarray<string,5> coll= {"hello", "world"};//print each element with its index on a linefor(int i=0; i<coll.size() ; i++)cout<< i<< ": "<< coll[i]<<endl;
}

一般array 的头文件是

#include<array>

以下声明式会建立一个array,带有5个类型为string 的元素:

array <string ,5> coll;

默认情况是这些元素都被元素的default构造函数初始化。这意味着,对基础类型而言, 初值不明确。

然后本程序使用了一个初值列,这东西允许我们以一系列值将对象初始化与创建期。自C++11起这种初始化方法受到每一种容器的支持,所以当然我们可以在vector和deque中使用它。 既然如此,基础类型用的是zero initialization,意味着基础类型保证被初始化为0.

注意,元素个数是array类型的一部分。因此array <int ,5> 和 array<int, 10> 是两个不同的类型,你不能对此两者进行复制或比较。

List

C++11起,STL竟然提供了两个不同的list容器:class list< > 和 class forward_list< > 。因此,list可能表示其中某个class,或者是个总体术语,代表上述两个list class。 然后就某种程度而言, forward list 只不过是受到更多限制的list , 现实中两者的差异并不这么重要。 因此当我使用术语list ,通常我指的是class list< > ,它的能力往往超越class forward_list< > 。

list< > 由双向链表(double linked list)实现而成。 这意味着list内的每个元素都以一部分内存指示其前导元素和后继元素。

list不提供随机访问,因此如果你要访问第10个元素,必须沿着链表依次走过前9个元素。不过,移动到下一个元素或着前一个元素的行为,可以在常量时间内完成。 因此一般的元素访问动作会发挥线性时间,因为平均距离和元素数量成正比。 这比vector 和deque提供的常量时间,效率差很多。

list的优势是:在任何位置上执行安插或删除动作都十分迅速,因为只需要改变链接就好。 这表示在list 中段处移动元素比在vector和deque快得多。

以下例子产生一个空list, 用以放置字符, 然后将‘a’ 到’z’的所有字符插入其中,利用循环每次打印并移除集合中的第一个元素,从而打印所有元素:

#include<bits/stdc++.h>
using namespace std;int main(){list<char> coll;// list container  for character elements//append elements from 'a' to 'z'for(char c='a';c<='z';c++)coll.push_back(c);//printfor(auto elem: coll){cout<< elem <<" ";}cout<<endl;
}

输出结果

a b c d e f g h i j k l m n o p q r s t u v w x y z

list需要包含头文件

#include<list>

以下定义了一个“元素类型为字符”的list:

list<char> coll;

为了打印所有元素,使用了一个基于范围的for循环,这种循环自C++11开始提供,允许对每个元素执行指定的语句。list并不提供作为“元素直接访问”指用的操作符[ ] 。这是因为list并不提供随机访问,因此操作符 [ ] 会带来低下的效率。

在此循环中,当前正在被处理的coll元素的类型被声明为auto 。因此elem的类型被自动推导为char,因为coll是个char集合。另一种做法是明白声明elem的类型:

for (char elem :coll){}

比如

 list<int> ll;for(int i=0;i<4;i++)ll.push_back(i);for(int elem:ll)cout<< elem<<" ";cout<<endl;

输出结果是 0 1 2 3

注意,elem永远是当前正被处理的元素的一个拷贝(copy)。虽然你可以改动它,但其影响只限于“针对此元素而调用的语句”,coll内部并没有任何东西被改变。 如果你想改动传入的集合的元素,你必须将elem声明为一个非常量的reference:

for(auto& elem : coll){...
}

就像函数的参数那样,通常你应该使用一个常量reference 以避免发生一次copy操作。因此下面的function template 输出“被传入的容器内的所有元素”:

template<typename T>void printElements(const T& coll){for(const auto & elem: coll)cout<<elem<<endl;
}

在C++11之前,打印所有元素的另一做法(不使用迭代器)是逐一地“打印而后移除”第一元素,直到此list之中不再有任何元素:

#include<bits/stdc++.h>
using namespace std;int main(){list<char> coll;for(char c='a';c<='z';c++)coll.push_back(c);//print//print and remove the first element while(!coll.empty()){cout<<coll.front()<<" ";coll.pop_front();} cout<<endl;
}

输出结果:

a b c d e f g h i j k l m n o p q r s t u v w x y z

成员函数empty()的返回值告诉我们容器中是否还有元素。只要这个函数返回false(也就是说,容器内还有元素),循环就继续进行:

while(! coll.emtpy() ){...
}

循环之内,成员函数front()返回第一个元素:

cout<< coll.front()<<" ";

pop_front()函数删除第一个元素:

coll.pop_front();

注意,pop_front()并不会返回被删除的元素,所以你无法将上述两个语句合二为一。

6.2.2 关联式容器(Associative container)

关联式容器依据特定的排序准则,自动为其元素排序。 元素可以是任何类型的value,也可以是key /value pair,其中key可以是任何类型,映射到一个相关value ,而value 也可以是任意类型。 排序准则以函数形式呈现,用来比较value,或比较key /value pair 中的key 。默认情况下所有容器都以操作符< 进行比较,不过你也可以提供自己的比较函数,定义出不同的排序准则。

通常关联式容器由二叉树实现出来。 在二叉树中,每个元素(结点)都有一个父节点和两个子结点。左子树的所有元素都比自己小,右子树的所有元素都比自己大。关联式容器的差别主要在于元素的种类以及处理重复元素时的方式。

关联式容器的主要优点是:它能很快找出一个具有某特定value 的元素,因为它具备对数复杂度,而任何循环式容器的复杂度是线性。 因此,使用关联式容器,面对1000个元素,平均而言只需要10此而不是500次比较动作。然后它的一个缺点是,你不能直接改动元素的 value,因为那会破坏元素的自动排序。

下面是STL定义的关联式容器:

  • set: 元素依据其value 自动排序,每个元素只能出现一次,不允许重复
  • multiset:和set的唯一区别是:元素可以重复。也就是multiset可包括多个“value相同”的元素。
  • map:每个元素都是key/value pair,其中key 是排序准则的基准。 每个key只能出现一次,不允许重复。map可被视为一种关联式数组(associative array),也就是“索引可以为任意类型”的数组。
  • multimap:和map的唯一区别是:元素可以重复,也就是multimap允许其元素拥有相同的key。multimap可被当作字典使用。

Set 和Multiset 实例

下面看一个例子,使用multiset:

#include<set>
#include<string>
#include<iostream>using namespace std;int main(){multiset<string> cities {"Braunschweig", "hanover", "Frankfurt", "New York","Chicago","Toronto" ,"Paris" ,"Frankfurt"};//printfor(const auto& elem: cities)cout<<elem<<" ";cout<<endl;//insert additional values:cities.insert({"London","Munich","Hanover","Braunschweig"});//printfor(const auto& elem: cities)cout<<elem<<" ";cout<<endl;
}

输出结果

包含头文件< set>后,可以声明cities是个以string为元素的multiset:

multiset< string> cities

声明之后,若干字符串以初值列形式成为元素初值。容器内部已经排好序。
multiset允许元素重复,如果声明成set,则不允许元素重复。

Map和Multimap 实例

下面的例子示范了如何使用map和multimap:

#include<map>
#include<iostream>
#include<string>using namespace std;int main(){multimap<int,string> coll;  // container for int/string values//insertcoll={{5,"tagged"},{2,"a"},{1,"this"},{4,"of"},{6,"strings"},{1,"is"},{3,"multimap"}};//printfor(auto c:coll){cout<< c.second<<" ";}cout<<endl;
}

运行结果
this is a multimap of tagged strings

包含< map >之后, 我们声明了一个map,其元素拥有一个int作为key和一个string作为value:

multimap<int,string> coll;

6.2.3 无序容器(Unordered container)

在无序容器中,元素没有明确的排列次序。 我们唯一关心的是,某个特定元素是否位于容器内。无序容器常以hash table实现出来,内部结构是一个 由linked list组成的array。 通过某个hash函数的运算,确定元素落于这个array的位置。hash函数运算的目标是:让每个元素的位置有助于用户快速访问任何一个元素,前提则是hash函数本身足够快。 基本上就是 数组和链表的搭配。


无序容器的主要优点是:当你打算查找一个带某特定值的元素,其速度甚至可能快过关联式容器。当你有一个良好的hash函数时, 查找速度为O(1)。 然后提供一个良好的hash函数并非易事,可能需要提供许多内存作为bucket。

STL定义出下面这些无序容器:

  1. unordered set 是无序元素的集合,其中每个元素只可出现一次。 不允许元素重复。
  2. unordered multiset 和 unordered set 唯一的差别是它允许元素重复。
  3. unordered map的元素都是key/value pair 。每个key只出现一次,不允许重复。
  4. unordered multimap和unordered map唯一区别是允许重复。也就是unordered multimap可能内含多个”拥有相同key“的元素。它可以用作字典。

unordered set/multiset 实例

下面是一个例子,使用unorder_multiset ,元素是string

#include<iostream>
#include<string>
#include<unordered_set>
using namespace std;int main(){unordered_multiset<string > cities {"Braunschweig", "hanover", "Frankfurt", "New York","Chicago","Toronto" ,"Paris" ,"Frankfurt"};//printfor(const auto& c:cities)cout<<c<<" ";cout<<endl;//insertcities.insert({"Shanghai","Beijing","Chongqing","Rizhao"});//printfor(const auto& s:cities)cout<<s<<" ";cout<<endl;}

输出结果

Paris Toronto Chicago New York Frankfurt Frankfurt hanover Braunschweig
Rizhao Chongqing Beijing Shanghai Frankfurt Frankfurt New York Braunschweig Chicago hanover Toronto Paris

打印所有元素,出现的次序可能不同于程序中所给的次序,因为其次序是不明确的。

unordered map/multimap实例

需要包含

#include<unordered_map>unordered_multimap<int,string>coll;

标准模板库之容器-《C++标准库(第二版)》读书笔记相关推荐

  1. 非类型模板参数(参考《C++ Templates 英文版第二版》)

    非类型模板参数(参考<C++ Templates 英文版第二版>) Chapter 3 3.1 非类型类模板参数 与前几章的简单例子不同,你也可以通过std::array实例化一个固定大小 ...

  2. 《Effective STL》读书笔记之容器1-5

    参考链接:http://blog.csdn.net/haotiangg/article/details/76266579 第一章 容器 第1条:慎重选择容器类型:选择合适的容器很关键! C++中各种标 ...

  3. 函数模板(参考《C++ Templates 英文版第二版》)

    C++模板编程(参考<C++ Templates 英文版第二版>) Chapter 1 函数模板 1.1 一窥函数模板 template<class T> T max(T a, ...

  4. 高效能沟《关键对话》读书笔记PPT模板-优页文档

    模板介绍 高效能沟<关键对话>读书笔记PPT模板-优页文档.一套,教育培训,读书笔记,幻灯片模板,内含青色多种配色,风格设计,动态播放效果,精美实用. 希望下面这份精美的PPT模板能给你带 ...

  5. 《高效学习7堂课》读书笔记PPT模板

    模板介绍 <高效学习7堂课>读书笔记PPT模板.一套读书笔记幻灯片模板,内含红色多种配色,风格设计,动态播放效果,精美实用. 希望下面这份精美的PPT模板能给你带来帮助,温馨提示:本资源使 ...

  6. C++ 笔记(19)— 标准模板库(STL容器、STL迭代器、STL算法、STL容器特点、STL字符串类)

    C++ 标准库可以分为两部分: 标准函数库: 这个库是由通用的.独立的.不属于任何类的函数组成的.函数库继承自 C 语言. 面向对象类库: 这个库是类及其相关函数的集合. C++ 标准库包含了所有的 ...

  7. vc2010多线程使用std标准模板库容器DEBUG版迭代器BUG

    在vc2010多线程环境下使用std标准模板库容器 list 时, 如果是DEBUG版本, 迭代器存在BUG 在<list>文件中,以下代码块会出现非法访问错,实际上是在子线程中调用lis ...

  8. c语言stl模板,c/c++开发分享C++ 标准模板库 STL 顺序容器详解

    c++ 标准模板库 stl 顺序容器 容器 顺序性 重复性 支持迭代器 vector 动态数组 无序 可重复 随机访问迭代器 deque 双向队列 无序 可重复 随机访问迭代器 list 双向链表 无 ...

  9. 标准模板库(STL)学习指南之List容器

    原文地址:http://dozb.bokee.com/1872684.html [文章导读] STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件 [正文] 什么是STL呢?STL就是St ...

  10. STL 标准模板库—容器部分【C++】

    STL标准模板库 包含内容: 容器类:vector.list.deque.set.map等 迭代器:"泛型指针",每个容器都有自己的迭代器,[vector和deque的迭代器是随机 ...

最新文章

  1. 全卷积神经网路【U-net项目实战】U-net网络结构为什么在医学影像分割上表现不错
  2. Spring Boot + 微信小程序——登录凭证校验DEMO
  3. mysql备份文件0kb_Oracle 数据文件大小为0kb或者文件丢失恢复
  4. 如何判断网通、电信、铁通IP地址分配段
  5. python编程软件排行榜_Python编程开发工具:这10个对Web开发者最有用的Python包
  6. TensorFlow2.0:误差计算
  7. R 语言基本操作(基本信息的查看、与本地文件系统交互、编译器版本升级)
  8. 安装双系统:Win7/Win10 + Ubuntu(亲测可用)
  9. 视频播功能及画面协同操作注意事项
  10. tensorflowgpu利用率为0_「活动」体验新一代主机 天翼云数十款云产品0元试用
  11. 3DSMAX和ZBRUSH打造神秘性感美女
  12. 小米路由php5.6,MT7621超频1100Mhz,带小米路由器3G/小米AC2100/红米AC2100固件下载
  13. 从输入URL到页面展现的全过程
  14. 斯坦福计算机科学博士研究方向,美国人工智能专业Top10名校推荐
  15. Android 微信登陆的坑
  16. Graphics详解
  17. Android power键亮屏流程
  18. VSAN磁盘组更换硬盘
  19. 根据二叉树先序遍历和中序遍历构建二叉树
  20. 关于浏览器访问servlet404异常

热门文章

  1. react 实现数据双向绑定
  2. scrollBarStyle- listview滑动条调整
  3. 如何處理不同版本的 Gem 執行檔,以 Rake 0.9.0 地雷為例
  4. rgb565和rgb555的文件头区别_Windows可执行文件格式
  5. java 排序_Java中常见的排序算法有哪些?---选择排序
  6. att48数据集最优值10628的解
  7. c语言表现一些简单的图片,C语言的一些简单例题.doc
  8. 【控制】《多智能体系统一致性与复杂网络同步控制》郭凌老师-第9章-结论与展望
  9. Win10系统下Visio安装失败问题
  10. 二、stm32f103+enc28j60