-------大纲(Outline)-------
STL 包含由5大模块组成:容器(container),迭加器(iterators),算法(algorithms),函数对象(function objects),配置器(allocators).
Section1: Sample
分别用不同的例子简单描述上述5大组件(components).
Section2:Philosophy
解释STL框架的原理
Section3:Componets.
详细介绍5大组件(components)
Section4:Extending STL
介绍怎样自己定义STL 模板
Example
大多数情况下我们需要一个类似数组(array)的能自动分配空间的类型,STL 有个vector ,下面举例说明vector怎样工作的,
将一组interger 类型的数据进行排序(sort),并打印(print)
version1: Standart C++
#include <stdlib.h>
#include <iostream.h>
// a and b point to integers. cmp returns -1 if a is less than b,
// 0 if they are equal, and 1 if a is greater than b.
inline int cmp(const void *a,const void *b)
{
int aa=*(int *)a;
int bb=*(int *)b;
return (aa<bb)? –1: (aa>bb)?1 : 0;
}
// Read a list of integers from stdin
// Sort (c library qsort)
// Print the list
void main(int argc,char *argv[])
{
const int size=1000; //array of 1000 integers
int array[size];
int n=0;
//read an integer into the n+1 the element of array
while(cin>>array[n++]);
n--; //it got incremented once too many times
qsort(array,n,sizeof(int),cmp);
for(int i=0; i<n;i++)
count<<array[i]<<”/n”;
}
Version2: containers ,iterators, algorithms
STL提供了大量能够包含其他对象的容器类(container),vector 就是其中一种,他类似于数组(array),能够需要自增长度,比如它的一个操作(operation) push_pack,将元素(element)加在vector(向量)的末尾,长度加1。
Vector 是一种sequence container,支持随机访问元素,常量时间内在尾端安插和移除元素,以及线性时间内在开头,中间处安插或移除元素.vector 的元素个数能够动态变更,其内存管理行为完全自动.【Generic Programming and the STL】
Vector 的大小(size,其所包含的元素个数)与容量(capacity,其内存所容纳的元素个数)之间有重要的区别.
所以,你可以用STL sort实现来替代上例的qsort实现,STL提供很多算法(algorithms), sort 是通用的,它可以在不同类型的容器(containers)中工作,因为它不是直接依赖容器(container)而是迭代器(iterators).
Preview of iterators
在后面我们详细介绍iterators, 它为容器提供了一种元素的位置指针服务,iterator 可以增加也可以减少,而且可以比较,这种特殊迭代器我们把它叫做”past-the-end”;
可以通过iterator 指向vector 的开始(begin)和结尾(end).
Vector<int> v;
//add some integers to v
vector::iterator i1=v.begin();
vector::iterator i2=v.end();
排序(sort) 就是在两个iterators 指定的范围(rang)操作,比如通过增加i1(i1++),只要小于iT2,注意,它是一个semi-open rang 半闭环区
例子:
#include <string.h>
#include <algo.h>
#include <vector.h>
#include <stdlib.h>
#include <iostream.h>
void main()
{
vector<int> v;
int input;
while(cin>>input) //while not end of file ENTER key
v.push_back(input);
// sort takes two random iterators, and sorts the elements between
// them. As is always the case in STL, this includes the value
// referred to by first but not the one referred to by last; indeed,
// this is often the past-the-end value, and is therefore not
// dereferenceable.
sort(v.begin(),v.end());
int n=v.size();
for(int I=0;I<n;I++)
cout<<v[I]<<”/n”;
}
Incidentally,sort 比 qsort 实现的速度要快!
Version 3: 迭代器配接器(iterator adaptors)
STL 事先定义的iterator大多数能迭代于container元素之中,但这并非必要条件,iterator的概念极为一般性,他们可用来表现“以任何次序安排“的数值(而非只是container中已排序好的数值)身上的迭代动作,STL 的stream iterators,就是基于C++ 的stream I/O Library实现输入与输出动作的。
STL 提供了一个配置器(adaptors),他能够实现将每个接口转换成另一种接口。Istream_iterator 其实就是一个有用的adaptor,它是一个template type,我们可以参数化想要处理的流(stream),比如 integers: 可以用 istream_iteraotr<int>. 一旦stream 结束,istream_iterator 便有一个特别的”stream 结束值”,此值就是 past-the-end iterator.
Istream_interator<int>(cin) 动作示意图:
为了讲标准输入的元素读入到一个vector中,我们需要用到copy算法(algorithm),copy包含3个iterators,第一和第二个指定(specify)源区间(source rang),第三个指定目标(destination):
int main()
{
vector <int > V;
copy(istream_iterator<int> (cin),istream_iterator<int>(), back_inserter(V));
}
也可以用:
typedef istream_iterator<int> istream_iterator_int;
algorthm 可以改成
copy(istream_iterator_int(cin),istream_iterator_int(),v.begin());
NOTE:有一个小问题就是如果第三个参数用v.begin(),假设,v没有足够的空间来存取所有的元素,这时 iterator 讲破坏past-the-end iterator 规则,引起错误。因为copy没有办法将新元素插入V中,其实是一种覆盖(overwrite)操作,就是讲新赋值于V既有的元素上,而不是插入新的元素,要实现插入新元素,就必须调用V的member function.其实我们用insertion操作来替代overwriting,利用另外一个adaptor做这些,back_insert_iterator,
它包含一个参数表示你要插入的对象类型:
back_insert_interator(v)动作示意图:
所以一个input动作可以描述:
typedef istream_iterator<int,ptrdiff_t> istream_iterator_int;
vector<int> v;
istream_iterator_int start (cin);
istream_iteratro_int end;
back_insert_iterator<vector<int>>dest (v);
copy(start,end,dest);
同样,输出动作也可以用copy algorithm;
copy(v.begin(),v.end(),ostream_iterator<int>(cout,”/n”);
ostream_iterator 是另外一个提供输出功能的adaptor,constructor包含两个实参;第二个参数是字符串,用于每个元素打印之后被打印
例:
#include <string.h>
#include <algo.h>
#include <vector.h>
#include <stdlib.h>
#include<iostream.h>
void main()
{
vector<int>v;
istream_iterator<int,ptrdiff_t> start (cin);
istream_iterator<int,ptrdiff_t> end;
back_insert_iterator<vector<int> >dest (v);
copy(start,end, dest);
sort(v.begin(),v.end());
copy(v.begin(),v.end(),ostream_iterator<int>(cout,”/n”));
}
结论:
在STL算法(algorithms)依赖于一个区间的概念(Rangs),它是贯穿了整个容器组件(container).这样,一般容器(container)都一个了一些基于区间的操作,比如,STL有个count algorithms,它用于计算[ first, last 区间与value相等的元素的个数。
template <class InputIterator, class T, class Size>
void count (InputIterator start, InputIterator end, const T& value, Size& n);
在区间内符合*I==value,的iterator的个数加入n中
所以必须要产生一个局部变量n,并讲它初始化0;
C++ Standart[ISO98]版中,提供了另外一种简单的实现
template<class InputIterator,class EqualityComparable>
typename iterator_traits<InputIterator>::difference_type
count(InputIterator first,InputIterator last,const EqualityComparable& value);
Section2:原理 (Philosophy)
STL编程我们讲它称为泛型编程(generic programming),它提供使得尽可能满足一般的参数类型的一系列算法。
STL 组件:
Containers
容器就是能够用于包含其他对象,并且能够确定这些被包含对象的基本属性,而不依赖于具体的对象类型。
就是数据结构,例如:
vectors,lists,deques,sets,maps,multisets,mulitimaps,queues,stacks,and priority queues…
下图给出我们常用的一些容器的抽象层次(hierarchy):
序列(Sequences)
Sequence 是一个大小可变的container,它不会以某种规定来排列元素,它必须具有两个member function insert 与erase,如果 s 是Sequence,而p是”指向s内的某个元素”的iterator,则 s.erase(p)会移除并释放改元素,s.insert(p,x)会产生一个新对象,此对象是x的一个副本,然后被安置于p所指元素之前,注意是之前而不是之后,因为iterator的range是非对称的,只有所谓”past-the-end”的元素,没有所谓”before-the-beginning”的元素,可以在s.begin()之前做插入动作,也可以在s.end()之后做插入动作,前者在最前头处插入一个元素,后者在最尾端处插入一个元素,如果s.insert(p,x)是将x插入在p之后,而非之前的化,那么上面的行为就没法达到了。
虽然2个member function 很简单,但存在两个重要问题:
1. 当进行插入和erase操作时,对指向Sequence的那个iterator有没有影响?
2. insert,erase的复杂度如何?
这两个问题,我们只能视情况而定,不同的Sequence,有个不同的体现。
STL包含3个Sequences类:
Vector:
在尾端插入很快,而且允许随机访问。
Lists
它是以节点(node)为基础的container,插入任何位置的速度都很快,但只提供顺序访问
Deques:
在尾端插入很快, 而且允许随机访问。但它具有类型限制。而且许多操作不支持.
Stacks and queues,同上
(关联性容器)Associative containers
Associative container 是一种可变大小的container,可以高效率的根据“以key为基础“的元素。它的每个元素有一个相关联的key,只要给定一个key,Associative container会很快查询key 的元素,
Associative container的基本操作是查询(lookup),插入(insert)和删除(erase).它不允许讲元素插入到Associative container的任意位置。
Sets, Multisets,Maps,Multimaps都是Associative container.
Iterators
前面已经描述了Iterator的意思,它实际上是用于指定容器或流的位置,类似于指针操作。它能够遍历由object所形成的range之中。有了iterator,得以将container和作用于它上的algorithmes 分离。
Input iterators
Input iterators 用于访问数据源。
Output iterators
它是用来存储数值序列。
Forward iterators
Forward iterator符合线性数值序列(liner sequence of values)的只觉概念,它不同于input iterator,也不同于output iterator,可用于多回(multipass)算法,顾名思义,它只能在range中向前移动。
Bidirectional iterators
它是一个可以incremented 也可以decremented的iterator.
Random access iterators
它是一中可以incremented 也可以decremented的iterator,并在向前或向后移动任意步数时,以及在比较两个iterators时,耗用固定的时间.它提供类似C指针的所以操作能力。