今天看了一些关于STL的内容,但是并没有太多看懂。。。
有很多东西还是不了解,只是了解了一些大面上的知识点:
下面是通过一些博客及文件整理了一些关于STL的部分知识:
STL体现的是泛型编程的核心思想:独立数据结构和算法。
STL主要由几个核心部件组成,即迭代器、容器、算法、函数对象、适配器。
容器即物之所属;算法是解决问题的方式;迭代器是对容器的访问逻辑的抽象,是连接算法和容器的纽带;迭代器通过添加了一种间接层的方式实现了容器和算法之间的独立;函数对象,就是重载了operator()操作符的对象;适配器是通过组合特定的容器实现的一种新的数据结构。
迭代器
STL中的迭代器是C++指针的泛化,它在算法和容器之间充当一个中间层,为处理不同的数据结构提供了一致的方式。迭代器也可以看作是对容器数据结构访问的一种约束。STL中的迭代器可分为类:随机存取迭代器(random-access-iterator),双向存取迭代器(bidirectional-access-iterator),前向迭代器(forward iterator),输入迭代器(input-iterator),输出迭代器(output-iterator)。
一、输入迭代器
输入迭代器也可以称之为前向的只读访问器,首先它提供了对容器的只读访问,其次它只能在容器中进行前向迭代(即只提供++操作)。所有的容器的迭代器都具有输入迭代器的特征。
二、输出迭代器
输出迭代器也可以称之为前向的只写访问器,首先它提供了对容器的只写访问,其次它只能在容器中进行前向迭代(即只提供++操作)。
三、前向迭代器
前向迭代器继承自输入和输出迭代器,因此具有输入和输出迭代器的所有特征,也即提供对容器数据结构的读写访问但也只具有前向迭代的能力(即只提供++操作)。
四、双向存取迭代器
双向存取迭代器从前向迭代器继承过来,因而具有前向迭代器的所有特征,双向存取迭代器还具有后向访问能力(即只提供–操作)。
五、随机存取迭代器
随机存取迭代器从双向存取迭代器继承过来,因而具有双向存取迭代器的所有特征。所不同的是,利用随机存取迭代器你可以对容器数据结构进行随机访问。
容器
容器即物之所在。容器是STL的核心部件之一,是迭代器的依附,是算法作用的目标。
STL中的容器可分为顺序容器(Sequence Container)和关联容器(Associative Container)。
容器适配器(Container Adaptor)是对顺序容器(Sequence Container)或关联容器(Associative Container)进行包装而得到的一种具有更多约束力(或功能更强大)的容器。
所有的容器都是物之所在,这就决定了它们必然存在很多共性,这些共性包括迭代器、大小等属性。容器与容器之间的主要区别体现在对数据的操作上。
每类容器都包含四个迭代器:iterator(正向迭代器)、const_iterator(常正向迭代器)、reverse_iterator(反向迭代器)、const_reverse_iterator(常反向迭代器)。
所有容器是数据的存在之处,可以看作的是数据的集合,因此它们都会有大小、是否为空等属性。
一、顺序容器
顺序容器中所有的元素在容器中的物理位置都是按照特定的次序进行存放的,区别于关联容器的是顺序容器中的元素的位置都是既定的。被纳入STL标准的顺序容器包括vector、list、dequeue。
序列容器之间的共性除了容器之间应有的共性之外,还有对数据操作的接口(非实现)上。
1、vector
vector和数组具有同样的内存处理方式。不同于数组的是:数组是静态空间,一旦分配了就不能被改变,因而空间的分配非常地不灵活;vector是动态空间,即空间可以被动态分配,因而空间的分配很灵活。可以说vector是相对数组的一种更高级的数据结构。
vector中的迭代器的种类为随机存取迭代器(random-access-iterator)。
vector不同于其它顺序容器(Sequence Container) 的是,它具有capacity属性,可以通过vec.capacity()来获取。
2、 list
list是链表的抽象数据结构(ADT)。list中的所有数据在空间分配上不一定是连续存放的。相对vector,list没有capaciy属性。
list中的迭代器的种类为双向存取迭代器(bidirectional -access-iterator)。
3、deque
deque,即双向链表。相对vector,deque也是连续空间,但不是vector的连续线性空间。
deque中的迭代器的种类为随机存取迭代器(random-access-iterator)。
二、关联容器
关联容器(Associative Container)提供了根据key快速检索数据的能力。在关联容器(Associative Container)中,key和元素都是成对(pair)存在的,你可以调用std::make_pair使用key和元素值来构建一个pair。
STL提供的关联容器包括set、multiset、map、multimap。
set和map只支持唯一键(unique key),即对个key最多只保存一个元素。multiset和multimap则支持多个key,一个key可以对应多个元素。
set和map的区别在于,在set里面key和元素是同一个值,而在map里面key和元素分开存储。
1.set
set是集合的抽象数据结构(ADT)。不同于数学意义上的集合,STL中的set的所有的元素都是有序的而且set中所有的元素都是唯一的。
set中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
2.multiset
multiset和set基本相同,所不同的是,一个multiset中的元素是可以重复的。
multiset中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
multiset的操作同set。
3.map
map是字典的抽象数据结构(ADT)。map中的所有的元素都会根据key自动进行排序,而且所有的元素都是唯一的。map中的所有的元素都是pair,即键(key)和值(value)组成的序列(pair中的第一个元素为key,第二个元素为value)。
map中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
4. multimap
multimap和map基本相同,所不同的是,一个multimap中的key可以重复。
multimap中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
multimap的操作同set。
5. 其它
这里要提到的就是hashtable,即哈希表的抽象数据结构(ADT)。
因为hashtable不是STL标准的一部分,部分编译器(如Microsoft Visual C++)并没有提供hashtable的实现。
hashtable中的迭代器的种类为前向迭代器(forward-iterator)。
在SGI的STL中,有hashtable的实现,其中的hash_set,hash_map,hash_multiset,
hash_multimap都是基于hashtable而构建的,是hashtable的适配器(Adaptor)。而在
Microsoft Visual C++的STL中,并没有提供STL的实现。其中的hash_set,hash_map,hash_multiset,hash_multimap都是基于STL中的hash算法而构建的。
hash_set的操作基本同set;
hash_map的操作基本同map;
hash_multiset的操作基本同multiset;
hash_multimap的操作基本同multimap。
算法
STL中的算法以迭代器为参数,通过作用于迭代器而达到处理容器数据的目的。所有的算法都前两个参数都是以对迭代器(iterator),通常称为first和last。事实上,算法所处理的迭代器范围为[first,last),如果first==last则表明此范围为空。按照处理问题的不同,STL算法可分为四组,分别是:
-
不改变序列的算法(None-mutating sequence Algorithm)
-
改变序列的算法(Mutating sequence Algorithm)
-
排序以及相关算法(Sorting and related Algorithm)
-
常用数字算法(Generalized numeric Algorithm)
很多算法都有if版本,如remove_if,这类算***根据判断条件进行相应的操作;有一些算法具有copy_if或copy版本,不同于非copy_if或copy版本的是,这些操作将获取的值改写另外的迭代器。
1.改变序列的算法
这类算法以输入迭代器(input-iterator)为输入参数,但不改写迭代器迭代器范围内的数据。典型的算法有:find、count、equal、search、for_each等。
2.不改变序列的算法
这类算法以输入迭代器(input-iterator)为输入参数,往往还带有一个输出迭代器(output-iterator),对输入迭代器(input-iterator)处理的结果写入输出迭代器。典型的算法有:copy、transform、remove、rotate、reverse、fill、replace等。
3. 排序以及相关算法
这类算法往往以双向迭代器(bidirectional-iterator)为输入输出参数,对迭代器范围内的数据进行排序,最终序列里面的值都会发生改变。之所以把排序以及相关算法和”不改变序列的算法”分离出来是因为这类算法解决了是算法领域的一类问题,而且这类问题在算法领域的重要性异常突出。典型的算法有:sort、partial_sort、nth_element、qsort、stable_sort、partition、merge、inplace_merge等。
4. 常用数字算法
这类算法往往以双向迭代器(bidirectional-iterator)为输入输出参数,对迭代器范围内的数据进行数字操作(如内积等),最终序列里面的值都会发生改变。这类算法解决的是数学领域相关的问题,因此被划为一类。典型的算法有:next_permutation、pre_permutation、inner_product等。
适配器(Adaptor)
适配器(Adaptor)是为了增强功能或/和添加约束,对容器、迭代器、算法重新进行了包装的模板类。设计模式中的Decorate模式的基本思想是实现适配器(Adaptor)的理论/实践基础。在STL中,适配器可分为三类:容器适配器、迭代器适配器以及算法适配器。
一、容器适配器
容器适配器是对特定的容器进行Decorate而形成的新的模板类。常见的容器适配器有:
1.vector适配器
stack:堆栈,是一种FILO的数据结构。
2.deque适配器
priority_queue:优先级队列,队列里面的所有数据都是按照优先级进行排序的。
queue:单向队列。
3.list适配器
slist:单向链表。
4.hashtable的适配器
hash_map,hash_set,hash_multimap,hash_multiset
二、 迭代器适配器
迭代器适配器是对特定的迭代器进行Decorate而形成的新的模板类。常见的迭代器适配器有:
1.反向迭代器(reverse iterator)
相对迭代器(iterator),反向迭代器(reverse iterator)是按照相反的顺序对容器的数据进行访问的,如
vector::reverse_iterator ritBegin = vec.rbegin();
vector::reverse_iterator ritEnd = vec.rend();
2.插入迭代器(insert iterator)
插入迭代器简化了向容器中插入元素的工作,指定了向容器中插入元素的位置。STL中有三种插入迭代器:
n 前向插入迭代器
在容器的前面插入元素,如调用容器的push_front
n 后向插入迭代器
在容器的后面插入元素,如调用容器的push_back
n 任意插入迭代器
在容器的任意位置插入元素,如调用容器的insert
3.原始存储迭代器(raw storage itertor)
允许算法将其结果保存到没有初始化的内存中。
三、函数适配器
函数对象适配器是对某类特定函数对象进行Decorate而形成的新的模板类。常见的迭代器适配器有:
1.否定者 (negator)
STL中有两个否定者not1和not2,分别对一元和二元谓词(predicate)的执行结果取反。
2.绑定者(Binder)
STL中有两个绑定者binder1st和binder2nd(你可以通过调用函数bind1st和bind2nd分别构建这两个绑定者),分别将特定值绑定到函数对象的第1个和第2个参数s。
3. 函数指针函数适配器(Adaptors for pointers to function)
因为STL中的算法一般都使用函数对象作为参数。如果需要使用原始的函数指针用于这些算法的输入参数,那么我们就需要将这些原始指针转化成特定的函数对象。STL提供了这样的转换函数,包括:
n 转化成员函数:mem_fun,mem_fun_ref,mem_fun1,mem_fun1_ref
n 转化非成员函数:ptr_fun
STL深深地植根于C++的基础设施,这其中包括了内联、函数对象、函数模板、类模板等。
一、内联是C++中一种特殊的语言机制,使用inline来标识。C++在编译inline标识的函数时,将根据特定的规则将inline函数的代码直接插入到inline函数的调用位置,以此来提高程序的运行效率,但同时也在一定程度上导致了代码的膨胀。
二、函数对象,就是重载了operator()操作符的对象。相对函数指针,函数对象可以具有状态,更安全,更灵活,基本没有额外的开销。在STL中,函数对象经常被用作算法的输入参数或容器的实例化的参数。
三、C++中的模板是STL实现的技术基础。C++中的模板可分为函数模板和类模板。函数模板抽象了针对不同类型的同一性质的操作。如针对int/long/string的max操作。类模板抽象了针对不同类型的同一类事务。如针对int/long/string的Stack。