STL (一) 从整体上把握stl上的概念
泛型程序设计
泛型编程(generic programming):也称为通用编程,类属编程
目标是: 将程序写得尽可能通用.
将算法从数据结构中抽象出来,成为通用的.
算法是对数据处理的逻辑,不同的数据结构,可能都有相同数据逻辑
泛型的目的就是: 用相同的逻辑来对不同的数据结构,不同的数据类型来进行操作.
比如"冒泡"(Sort),用它来对不同的类型排序: 数组,链表,树. 而不是对每一种单独的数据,来做单独的排序算法.
泛型需要把类型来当作参数传递.
泛型就是把类型给"参数化",C++的模板就是这一技术的基石.
什么是STL
STL是一个高效的C++程序库. (Standard Template Library),即:标准模板库.
STL为C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可利用性.STL中包含了诸多在计算机科学领域里常用的基本数据结构和基本算法.
STL的可扩展性:我们可以在STL提供的框架之下 ,扩展出很多新的算法, 它能够适应现有的容器,或数据结构,那么这个新算法,就能对现有的所有数据结构进行操作.
从逻辑层次来看,在STL中体现了泛型化程序设计的思想(generic programming)
这种思想中,大部分基本算法被抽象,被泛化,独立于与之对应的数据结构,用于以相同或相近的方式处理各种不同情形.
从实现层次看,整个STL是以一种参数化(type parameterized)的方式来实现的.
基于模板(template)
有模板的不调用不实例化的特性
泛型把一种类型当参数传递,其实例化时,生成各种不同的类型
STL是泛型程序设计思想比较成功的一套产品
STL六大组件及其关系
容器(Container) 各种基本数据结构
有人一听说STL就想起来的组件,它是STL中最常用的组件,把STL等同于容器是片面的.
什么是容器: 比如: 数组 动态链表 集合 树 ; 等等,数据结构都是容器.
适配器(Adapter) 可改变containers Iterators 或Function object接口的一种组件
比如用deque vector list 来实现一个Stack,这个自定义的Stack里面有Push,Pop等方法.那么这个自定义的Stack就是一种容器适配器,里面的Push实际上是vector里面的push_back,只因为Stack是先进后出的.再如Pop的实现,就是借助了vector里的接口(方法)pop_back来实现; 如果我们换为实现队列呢,那么Pop的实现,就要借助pop_front这个接口来实现了,因为队列是先进先出的.
如果用其它的如迭代器或函数对象实现的适配器,那就称为迭代器适配器,或函数对象适配器.
算法(Algorithm) 各种基本算法如: sort search......
迭代器(Iterator) 连接容器和算法
函数对象(Function object)
以类模板的形式提供的,函数对象它使得类使用起来象一个函数.
一般这些类都要重载括号"()",才能使得类使用起来象是一个函数.
分配器(Allocator)
内存分配器,它提供了容器内部数据的内存分配方式.
这个内存分配方式,可以实现为内存池.
如: SGI STL当中的分配方式,是二级的内存池.
VC当中的分配方式,它不是以内存池的方式来实现的.
不同版本的STL,其内存的分配方式是有差异的.是不一样的.但它们都有一致的接口.
容器 算法 迭代器 关系图
容器为各种基本数据提供支持
算法独立于数据结构,独立于容器,它是一系列逻辑,它能操作各种数据结构
算法操作各种容器或数据结构时,有一个问题:数据结构各种各样,算法怎么就能操作数据结构呢,让算法逻辑得以运行?算法要操纵数据结构,就得遍历数据结构中的所有数据,但各个数据结构是不一样的,算法就不能做出各个数据结构的遍历逻辑,这就需要一种中间"胶水"----迭代器,各种容器都有统一了接口的迭代器,迭代器在各个容器内部都有,迭代器知道容器内部数据结构和数据的分布.那么,算法要操作各个容器和数据结构,就少不了迭代器这个桥梁.
算法不关心如何遍历容器与数据结构,这些遍历交给迭代器来完成.
迭代器来遍历容器,那么它又是如何知道容器内的数据结构的呢? 不同的容器有不同的数据结构,也就意味着需要不同的遍历方式,这个在于 : 每一种容器都提供自己的迭代器.
各种容器各自的迭代器,在外部的角度来看,各种容器都提供了一致的接口:如 ++ 向前遍历 -- 向后遍历 *号取出遍历到的元素,等,具有一致的接口.这些遍历的算法由容器自己去实现.
容器
容器类是容纳,包含一组元素或元素集合的对象
七种基本容器:
1 vector 向量
动态数组,连续有序的
内部空间连续
2 deque 双端队列
动态数组,一片片有序的,数据在每一片空间内是连续的
可以在两个端口进行操作:
3 list 链表
双向链表,两头都可以操作
4 set 集合
集合当中关键码key不能重复
5 multiset 多重集合
6 map 映射
除了关键码Key之外,还有值value: Key Value
map当中Key是不能重复的
7 multimap 多重映射
七种容器的分类:
序列式容器
每个元素均有固定位置----取决于插入时机和地点,和元素无关.
包括: vector deque list
关联式容器
元素的位置取决于特定的排序准则以及元素值,和插入的次序无关.
包括: set multiset map multimap
如何选择序列式容器
vector在尾部插入与删除效率高,但在头部与中间插入删除效率较低.
deque在头部与尾部插入与删除效率较高.
下标中间的访问速度也是比较快的,访问效率是常数级别的,不亚于它的空间大小O(n).
deque下标访问的算法复杂度是常数级的O(1).
list 插入和删除效率高,如需要频繁在序列中间位置上进行插入或删除操作,而且不需要过多的在序列内部进行长距离跳转,应该选择list
迭代器
Iterators(迭代器),是容器与算法的桥梁,用来在一个对象群集的元素上进行遍历.这个对象群集或许是一个容器,或许是容器的一部分.迭代器的主要好处是,为所有容器提供了一组很小的公共接口.迭代器以++进行累进,以*进行提领,因而它类似于指针,我们可以把它视为一种smart pointer
++操作可以遍历到群集内的下一个元素.至于如何做到,取决于容器内部的数据组织形式.
每种容器都提供了自己的迭代器,而这些迭代器能够了解容器内部的数据结构.
算法
算法 Algorithms, 用来自责群集内的元素.它们可以出于不同的目的而搜寻、排序、修改、使用那些元素。通过迭代器的协助,我们可以只需要编写一次算法,就可以将它应用于任意容器,这是因为所有的容器迭代器都有提供一致的接口。
适配器
适配器是一种接口类
为已有的接口提供新的接口
目的是简化、约束、使之安全、隐藏或者改变被修改类提供的服务集合。
三种类型的适配器:
1 容器适配器:用来扩展7种基本容器,它们和顺序容器相结合构成栈、队列和优先队列容器
2 迭代器适配器(反向迭代器、插入迭代器、IO流迭代器)
3 函数适配器(函数对象适配器、成员函数适配器、普通函数适配器)