VC++-标准模板库STL(Standard Template Library)

来源:互联网 发布:阿里云系统机顶盒root 编辑:程序博客网 时间:2024/05/22 15:19

1.STL简介

1.1标准模板库STL(Standard Template Library)

1.2C++ 基础

1.2.1类.

1.2.2函数对象(Function Objects).

1.2.3模板(Template)

2.容器(Container)

2.1向量(Vector)

2.2线性表(List)

2.3双向队列(Deque)

2.4关联容器(Container)

3.迭代器(Iterator)

 3.1 输入和输出迭代器

3.2向前迭代器

3.3双向迭代器

3.4任意存取迭代器

3.5迭代标签(Iterator Tag)

4. 算法和函数对象

4.1如何创建泛型算法

4.2STL 算法

5. 适配器(Adaptor)

 5.1 容器适配器

5.2迭代适配器

5.3函数适配器

6 其余的STL部件


1.STL简介

1.1标准模板库STL(StandardTemplate Library)

(1)STL部件库

STL是个部件库(component library) ,其中的部件包括有容器(container ,储存任意类型对象的对象)和算法。只要用户对自己定义的部件做点小小的要求,STL 中的算法就可以工作在用户自定义的容器上,或者STL 中的容器可以使用用户自定义的算法。

我们可以把软件部件想象成一个三位空间:

第一维表示数据类型(int, double, char, …);

第二维表示容器(array, linked-list, …);

第三维表示算法(sort, merge, search, …) 。

STL具体化了上述思想,期望通过减少开发时间以简化软件开发,简化调试、维护并增加代码的可移植性。

STL包含5 个主要的部分:

·算法(Algorithm) :能运行在不同容器(container) 上的计算过程。

·容器(Container) :能够保留并管理对象的对象。

·迭代器(Iterator):算法存取容器(algorithm-access to containers) 的抽象,以便算法可以应用在不同的容器上。

·函数对象(Function Object) :定义了函数调用操作符(operator()) 的类。

·适应器(Adaptor) :封装一个部件以提供另外的接口(例如用list实现stack)。

(2)STL使用

STL使用时很简单:

//MyFile.cpp#include“stdafx.h”…#include<vector> //include 你想用的STL头文件usingnamespace std; // 一定要写上这句话,因为STL 都是在std名字空间中定义void F1(){…vector<int> v(10); // 现在你就可以放心大胆的使用STL 了…}

1.2 C++ 基础

1.2.1 类.

请看下面一段代码:

class shape{private:    intx_pos;    inty_pos;    intcolor;public:    shape(): x_pos(0), y_pos(0), color(1) {}    shape(intx, int y, int c = 1) : x_pos(x), y_pos(y), color(c) {}    shape(constshape& s) : x_pos(s.x_pos), y_pos(s.y_pos), color(s.color) {}    ~shape(){}    shape&operator=(const shape& s)    {         x_pos= s.x_pos;         y_pos= s.y_pos;         color= s.color;         return*this;    }    intget_x_pos() { return x_pos; }    intget_y_pos() { return y_pos; }    intget_color() { return color; }    voidset_x_pos(int x) { x_pos = x; }    voidset_y_pos(int y) { y_pos = y; }    voidset_color(int c) { color = c; }    virtualvoid DrawShape() {}    friendostream& operator<<(ostream& os, const shape& s);    };ostream&operator<<(ostream& os, const shape& s){    os<< “shape: (“ << s.x_pos << “,” << s.y_pos << “,”<< s.color << “)”;    returnos;}

1.2.2 函数对象(FunctionObjects).

所谓函数对象(function object) 是定义了函数调用操作符(funiton-call operator ,即

operator())的对象。

请看下面的例子:

class less{public:    less(intv) : val(v) {}    intoperator()(int v)    {        returnv < val;    }private:    int val;};

含义:

声明一个less 对象:less less_than_five(5);

当调用function-call operator 时,看判断出传入的参数是否小于val:

cout<< “2 is less than 5: “ << (less_than_five(2) ? “yes” : “no”);

输出是:2is less than 5: yes

函数对象在使用STL 时非常重要,你需要熟悉这样的编码形式。在使用STL 时,经常需要把函数对象作为算法的输入参数,或着实例化一个容器(container)时的输入参数。

1.2.3 模板(Template)

(1)函数模板

请看下面的代码:

void swap(int& a, int& b){     inttmp = a;     a= b;     b = tmp;}

这个函数swap 很简单:把两个整数的值交换一下。但当你写一个大程序时,经常需要交换float、long 、char等变量,甚至如上面定义的shape 变量,这时候你就需要把上面的代码修改一下,适应各自的参数类型,然后拷贝到很多处各自需要的地方。一旦需要对这块代码做些改动的时候,就必须把所有用到这块代码的地方一一做相应的修改,解决这个问题的方法就是用模板(template):

template <class T>void swap(T& a, T& b){     Ttmp = a;     a= b;     b= tmp}

注意这里T是任意的类型名字。看例子:

int a = 3, b =5;shape MyShape, YourShape;float fa = 3.01, fb = 5.02;swap(a , b); // 交换整数swap(MyShape, YourShape); // 交换自定义类的两个对象的值swap(fa, fb); // 交换浮点数

还可以看看两个函数模板例子:

template <class T>T& min(T& a, T& b) // 取两个元素中的最小者{     returna < b ? a : b;}template <class T>void pritn_to_cout(char* msg, T& obj) // 把一个元素输出到cout 上{     cout<< msg << “: “ << obj << endl;}

使用后者时,要求类型T 中定义了put-to 操作符(operator <<)。

(2)类模板

创建类模板的动机和容器类的使用是密切相关的。比如一个容器类栈(stack),定义者并

不关心栈中对象的类型,而使用stack 者自己指定到底包含什么对象类型。

下面看一个向量(vector) 例子:

template <class T>class vector{    T*v;    intsz;public:     vector(ints) { v = new T[sz = s]; }     ~vector(){ delete [] v; }     T&operator[] (int i) { return v[i]; }     intget_size() { return sz; }};

现在可以实例化不同类型的vector 容器了:

vector<int> int_vector(10);vector<char> char_vector(10);vector<shape> shape_vector(10);

(3)模板特化

也许在某些情况下,编译器对某种类型产生的模板代码不另你满意,你也可以为这种类

型给出一个特定的实现,此之谓“模板特化(template specialization)”。比如,你想让shape

类型的vector只包含一个对象,则可以特化vector 模板如下:

class vector<shape>{      shape v;public:      vector(shape&s) : v(s) {}      shape&operator[] (int i) { return v; }      intget_size() { return 1; }};

使用时:

shape MyShape;vector<shape>single_shape_vector(MyShape);

STL 中有时会需要定义特定类型的算法或容器实现。


2.容器(Container)

容器(Container)是能够保存其他类型的对象的类。容器形成了STL 的关键部件。当书写任何类型软件的时候,把特定类型的元素集聚起来都是很至关重要的任务。

STL中支持的容器如下:

顺序容器(Sequence Container):向量(Vector)、双向队列(Deque)、线性表(List)。

关联容器(Associative Container):集合(Set)、多重集合(MultiSet)、映射(Map)、多重映射(MultiSet)。

顺序容器组织成对象的有限线性集合,所有对象都是同一类型。STL 中三种基本顺序容器是:向量(Vector) 、线性表(List)、双向队列(Deque) 。

关联容器提供了基于KEY 的数据的快速检索能力。元素被排好序,检索数据时可以二

分搜索。STL有四种关联容器。当一个KEY 对应一个Value时,可以使用集合(Set)和映射

(Map);若对应同一KEY 有多个元素被存储时,可以使用多集合(MultiSet)和多映射

(MultiMap) 。

2.1 向量(Vector)

如果你要将所有shape 对象保存在一个容器中,用C++代码可以这样写:

shape my_shapes[max_size];

这里max_size 是可以保存在my_shapes 数组中的最大数量。

当你使用STL 时,则可以这样写:

#include <vector>using namespace std;int main(){     vector<shape> my_shapes; // 使用my_shapes…    return0;}


想得到容器中能保存的最大元素数量就可以用vector 类的成员函数max_size():

vector<shape>::size_type max_size =my_shapes.max_size();

当前容器的实际尺寸 ---已有的元素个数用size():

vector<shape>::size_type size =my_shapes.size();

就像size_type 描述了vector 尺寸的类型,value_type 说明了其中保存的对象的类型:

cout << “value type: “ <<typeid(vector<float>::value_type).name();

输出:valuetype: float

可以用capacity() 来取得vector 中已分配内存的元素个数:

vector<int> v;vector<int>::size_type capacity =v.capacity();vector类似于数组,可以使用下标[]访问:vector<int> v(10);v[0] = 101;

注意到这里预先给10 个元素分配了空间。你也可以使用vector 提供的插入函数来动态的扩展容器。成员函数push_back() 就在vector 的尾部添加了一个元素:

v.push_back(3);

也可以用insert()函数完成同样的工作:

v.insert(v.end(), 3);

这里insert()成员函数需要两个参数:一个指向容器中指定位置的迭代器(iterator),一个待插入的元素。insert()将元素插入到迭代器指定元素之前。

现在对迭代器(Iterator)做点解释。Iterator 是指针(pointer)的泛化,iterator 要求定义

operator*,它返回指定类型的值。Iterator 常常和容器联系在一起。例子:

vector<int> v(3);v[0] = 5;v[1] = 2;v[2] = 7;vector<int>::iterator first =v.begin();vector<int>::iterator last = v.end();while (first != last)cout << *first++ << “ “;

上面代码的输出是:5 2 7

begin()返回的是vector 中第一个元素的iterator ,而end()返回的并不是最后一个元素的iterator ,而是pastthe last element 。在STL 中叫past-the-enditerator 。

begin() 返回的iteratorend()返回的iterator

区间[being(), end() ):

两个迭代器给定的区间在STL 中非常重要,因为STL 算法中大量的使用区间。比如排序算法:

 sort(begin_iterator, past_the_end_iterator);

第一个参数指向区间的第一个元素,而第二个参数指向区间的path-the-end 元素。

有了迭代器作为媒介,我们就可以把算法(algorithm) 和容器(container) 分开实现了:

可以用以下的几种方法声明一个vector 对象:

vector<float> v(5, 3.25); // 初始化有5 个元素,其值都是3.25vector<float> v_new1(v);vector<float> v_new2 = v;vector<float> v_new3(v.begin(),v.end());

这四个vector对象是相等的,可以用operator== 来判断。

其余常用的vector成员函数有:

empty() :判断vector是否为空

front():取得vector的第一个元素

back() :取得vector的最后一个元素

pop_back() :去掉最后一个元素

erase():去掉某个iterator或者iterator 区间指定的元素

到现在我们已经可以把一些对象存储在容器(container)中,并有几种手段来进行管理和

维护。

除了上面提到的vector 成员函数之外,它还有一个reserve()成员函数,用来预订给定的

向量尺寸。reserve可以减少存取元素时的内存重分配。

2.2 线性表(List)

和向量(vector) 不一样,线性表(list)不支持对元素的任意存取(即不通过下标[]存取)。list

中提供的成员函数和vector 类似,有begin 、end、rbegin、rend、push_back、pop_back,list 也提供对表首元素的操作push_front 、pop_front 。两个线性表可以用splice、merge合并在一起,也可翻转(reverse)和排序(sort),也可让表中同值的元素唯一(unique) 。

 

2.3 双向队列(Deque)

象向量vector 一样,双向队列(Deque) 支持任意存取。它提供的成员函数还有 push_front 、pop_front 、insert 、push、earse 、pop 等。

2.4 关联容器(Container)

关联容器(Associative Container) 提供了快速检索基于关键词(Key)的数据的能力。和序列容器(vector 、list、deque)一样,关联容器用来存储数据,而且设计关联容器时考虑到了优化数据检索的意图 ---通过关键词(Key)作为标识把单一的数据记录组织到特定的结构中(如tree)。STL 提供了不同的关联容器:集合(set)、多元集合(multiset) 、映射(map) 、多元映射(multimap)。

 set 和map支持唯一关键词(unique key) ,就是对每个KEY,最多只保存一个元素(数据记录)。multiset和multimap 则支持相同关键词(equal key) ,这样可有很多个元素可以用同一个KEY 进行存储。set(multiset) 和map(multimap) 之间的区别在于set(multiset) 中的存储数据内含了KEY 表达式;而map(multimap)则将Key 表达式和对应的数据分开存放。

假设现在要保存某公司里雇员的信息。雇员信息类定义如下:

class employee_data{public:    employee_data() : name(“”), skill(0),salary(0) {}    employee_data(string n, int s, long sa) :name(n), skill(s), salary(sa) {}    stirng name; // 雇员名字    int skill; // 雇员职称    long salary; // 雇员薪水    friend ostream&operator<<(ostream& os, const employee_data& e);};ostream& operator<<(ostream&os, const employee_data& e){    os << “employee: “ << e.name<< “ “ << e.skill << “ “ << e.salary;    return os;}

现在想把雇员数据保存在集合set(multiset) 中,关键词KEY 包含在被保存的对象中:

class employee{public:    employee(int ii, const employee_data& e): identification_code(i), description(e) {}    intidentification_code; // 标识雇员的关键词    employee_data description;    bool operator<(const employee& e)const    {        return identification_code <e.identification_code;    }};


现在我们声明雇员集合set(multiset):

set<employee, less<employee>>employee_set;

multiset<employee,less<employee>> employee_multiset;

此时,employee既是Key type 又是Value type 。

如果我们想把雇员信息保存在映射map(multimap) 中,则如下声明:

map<int, employee_data,less<int>> employee_map;

multimap<int, employee_data,less<int>> employee_multimap;

此时Keytype 是int ,而Valuetype 是employee_data 。

所有的关联容器都有以下成员函数:begin, end, rbegin, rend, empty,size, max_size, swap 、insert、erase 等,其意义同顺序容器一样。下面我们用插入函数insert 想关联容器中加入元素:

employee_data ed1(“john”,1, 5000); // 雇员John 的信息employee_data ed2(“tom”,5, 2000); // 雇员tom 的信息employee_data ed3(“mary”,2, 3000); // 雇员mary 的信息employee e1(1010, ed1); // 雇员John,证件号1010(KEY)employee e2(2020, ed2); // 雇员tom ,证件号2020(KEY)employee e3(3030, ed3); // 雇员mary ,证件号3030(KEY)employee_set.insert(e1); // 第一次,成功加入雇员John 的信息employee_set.insert(e1); // 第二次,不成功加入雇员John 的信息。因为已经存在了

假定John 和Tom 在同一部门(部门代号:101) 工作,而Mary在另一部门工作(部门代号:102)现在把这三位用multimap 来保存:

employee_multimap.insert(make_pair(101,ed1)); // 101 部门的Johnemployee_multimap.insert(make_pair(101,ed2)); // 101 部门的Tomemployee_multimap.insert(make_pair(102,ed3)); // 102 部门的Mary

那么现在在101部门工作的雇员就有2 人了:

multimap<int, employee_data,less<int>>::size_type count = employee_multimap.count(101);

有关关联容器成员函数的详细定义请参考STL 源码。

3.迭代器(Iterator)

为了将算法应用到容器中的元素上,下面要对迭代器(Iterator)做进一步的解释。

迭代器(Iterator)是指针(pointer) 的泛化,它允许程序员以相同的方式处理不同的数据结构(容器)。STL 中有5中类型的迭代器:任意存取迭代器、双向迭代器、向前迭代器、输入迭代器、输出迭代器。它们分别满足一定的要求。不同的迭代器要求定义的操作不一样。比如某个算法需要一个双向迭代器(Bidirctional Iterator) ,你可以把一个任意存取迭代器(Random Access Iterator) 作为参数;但反之不行。

3.1 输入和输出迭代器

输入迭代器(Input Iterator) 需要满足的条件:

·constructor

·assignment operator

·equality/inequality operator

·dereferenc operator

·pre/post increment operator

输入迭代器表示要从其中取出一个值,即从输入迭代器X 中取值只可有以下三种形式:

V = *X++

V = *X, ++X

V = *X, X++

输出迭代器(Output Iterator)需要满足的条件:

·constructor

·assignment operator

·dereferenc operator

·pre/post increment operator

一个输出迭代器X 只能有一个值V 存储其中,在下一个存储之前,必须将X 加一。即只可有以下三种形式之一:

 *X++=V

*X = V, ++X

*X = V, X++

注意,对输入和输出迭代器,一旦取出或放入值后,就只能前进(increment),不可多次取出

或放入。

3.2 向前迭代器

向前迭代器(Forward Iterator)需要满足的条件:

·constructor

·assignment operator

·equality/inequality operator

·dereferenc operator

·pre/post increment operator

和输入和输出迭代器比较而言,两个向前迭代器r 和s,若r == s 则++r== ++s 。而且既可取值,也可赋值:*X = V,V = *X 。看一个例子:

template <class ForwardIterator, classT>ForwardIterator find_linear(ForwardIteratorfirst, ForwardIterator last, T& value){    while(first != last)    if(*first++ == value)        returnfirst;    return last;}

函数find_linear()在一个容器中循环,如果找到指定的值,则返回该值的迭代器位置;否则返回past-the-end 迭代器。下面用这个函数:

vector<int> v(3, 1);v.push_back(7); // vector v: 1 1 1 7vector<int>::iterator i =find_linear(v.begin(), v.end(), 7);if (i != v.end())cout<< *i;elsecout<< “NOT FOUND”;

输出结果:7

3.3 双向迭代器

在向前迭代器的基础上,双向迭代器(Bidirectional Iterator) 还满足以下需求:

·pre/post decrement operator

即双向迭代器不仅允许++,而且允许--。它允许一个算法走过(pass through) 容器中的元素时,即可想前走,也可向后走。

我们看一个使用双向迭代器的多遍走过(multi-pass) 算法 ---冒 泡排序(Bubble Sort):

template <class BidirectionalIterator,class Compare>void bubble_sort(BidirectionalIterator first,BidirectionalIterator last, Compare comp){    BidirectionalIteratorleft_el = first, right_el = first;    right_el++;    while(first != last)    {        while(right_el != last)        {             if(comp(*right_el, *left_el))             iter_swap(left_el, right_el);             right_el++;             left_el++;         }         last--;         left_el= first;         right_el= first;         right_el++;     }}

二元函数对象Compare 由用户提供,得出两个参数的断言结果(true/false)。用法:

list<int> l; // list 类似于vector ,但不支持下标[]存取

// 填充list

bubble_sort(l.begin(), l.end(),less<int>()); // 递增排序bubble_sort(l.begin(), l.end(),greater<int>()); // 递减排序

3.4 任意存取迭代器

在双向迭代器的基础上,任意存取迭代器(Random Access Iterator) 还满足以下需求:

·opetrator+(int)

·opetrator+=(int)

·opetrator-(int)

·opetrator-=(int)

·opetrator-(random access iterator)

·opetrator[](int)

·opetrator>(random accessiterator)

·opetrator<(random accessiterator)

·opetrator>=(random accessiterator)

·opetrator<=(random accessiterator)

也就是说,任意存取迭代器可以“任意的”前进和后退。

3.5 迭代标签(IteratorTag)

每个迭代器(iterator)必须定义表达式iterator_tag(),它返回该迭代器的类别标签(category

tag)。

 STL 中有5个迭代器标签:input_iterator_tag 、output_iterator_tag 、forward_iterator_tag、

bidirectional_iterator_tag 、random_access_iterator_tag 。

迭代器标签被用在编译时选择效率最高的算法(因为几乎所有的算法都用到迭代器)。


4. 算法和函数对象

STL库中的算法都以迭代器类型为参数,这就和数据结果的具体实现分离开了。基于此,这些算法被称作泛型算法(generic algorithm) 。

4.1 如何创建泛型算法

这里给出一个二分搜索的基本算法(generic bianry search algorithm) ,可以看看基本算法

的实现思路。给定一个排好序的整数数组,要求二分搜索某个值的位置:

const int* binary_search(const int* array,int n, int x){    const int* lo = array, *hi = array+n, *mid;    while (lo != hi)    {         mid =lo+(hi-lo)/2;         if (x == *mid)             return mid;         if (x < *mid)              hi = mid;         else              lo = mid+1;     }     return 0;}

为了让上述算法对任意类型的整数数组起作用,下面将之定义成模板函数:

template <class T>const T* binary_search(const T* array, int n,const T& x){    const T* lo = array, *hi = array+n, *mid;    while (lo != hi)    {         mid =lo+(hi-lo)/2;         if (x == *mid)              return mid;         if (x < *mid)              hi = mid;         else              lo = mid+1;       }      return 0;}

在上面的算法中,如果没有找到指定的值,则一个特殊的指针NULL (0) 被返回了,这就要求这个值存在。我们不想做这样的假定这样的值,可以不成功的搜索返回指针array+n (就是past-the-end) 来代替:

template <class T>const T* binary_search(const T* array, int n,const T& x){    const T* lo = array, *hi = array+n, *mid;    while (lo != hi)    {         mid =lo+(hi-lo)/2;         if (x == *mid)             return mid;         if (x < *mid)             hi = mid;         else             lo = mid+1;     }     return array+n;}

为了代替给定数组及尺寸,我们可以给定第一个及past-the-end 元素的指针:

template <class T>const T* binary_search(T* first, T* last,const T& value){    const T* lo = first, *hi = last, *mid;    while (lo != hi)    {        mid = lo+(hi-lo)/2;        if (value == *mid)             return mid;        if (value < *mid)             hi = mid;        else              lo = mid+1;     }    return last;}


现在可以看出,first 和last 两个指针是任意类型的指针,即它们和类型T 是没有关系的。这时就用到了迭代器(Iterator) 的概念,因为这里排序要对容器任意存取,我们把first 和last 的类型命名为“RandomAccessIterator”。代码改写如下:

template <class RandomAccessIterator,class T>RandomAccessIteratorbinary_search(RandomAccessIterator first, RandomAccessIterator last,const T& value){    RandomAccessIterator not_found = last, mid;     while (first != last)     {           mid = first+(last-first)/2;           if (value == *mid)                return mid;           if (value < *mid)                 last = mid;           else                first = mid+1;       }      return not_found;}

上面这个基本二分搜索算法即使对内部类型(build in types) 功能也一样:

int x[10]; // 10 个整数数组int search_value; // 太搜索的值// 初始化变量int* i = binary_search(&x[0], &x[10],search_value);if (i == &x[10])cout << “value NOT FOUND”;elsecout << “value found”;

所有STL 中的算法都是用以上类似的办法定义的。

4.2 STL 算法

STL中的算法可以分成四组:组(Group) 算法类型(Algorithm Type)

1不改变顺序的操作(Non-mutating sequence Operations)

2改变顺序的操作(Mutating sequence Operations)

3排序及相关操作(Sorting and related Operations)

4常用的数字操作(Generalized numeric Operations)

Group1中的操作不改变容器中元素的顺序,而Group2 中的要改变。当然Group3 排序操作也会改变元素的顺序,但把排序相关的操作独立于Group1 列出来了。Group4中是些常用的数字操作。

先看个Group1 中的一个算法“对每个(for_each)”,有三个参数,两个输入迭代器,一

个函数对象。这个操作的意思是对[first, last)的每个元素都做一个Function 操作:

template <class InputIterator, classFunction>Function for_each(InputIterator first,InputIterator last, Function f) {    while (first != last) f(*first++);    return f;}下面给一个使用for_each 的例子:template <class T>class sum_up{public:    void operator() (const T& value) { sum +=value; }    const T& read_sum() { return sum; }private:    static T sum;};int sum_up<int>::sum = 0; void main(){    deque<int> d(3, 2); // 两个元素:3 3    sum_up<int> s;    for_each(d.begin(), d.end(), s);    cout << s.read_sum();}

输出结果:6。注意到这里用到了函数对象sum_up,它定义了operator 。所以函数对象是STL 中一个非常重要的概念,屡屡用到。

 Group1 中还有其他的操作,如“寻找(Find)”、“邻居寻找(Adjacent find)”、“计数

(Count)”、“不匹配(Mismatch)”、“相等(Equal)”、“搜索(Search)”等。

说明,如果某个算法的后缀是_if,表示它自己提供断言(Predicate) 函数,比如find 算法

有find_if的变种。

Group2中的有算法“拷贝(Copy)”、“交换(Swap)”、“变换(Transform)”、“替换

(Replace)”、“填充(Fill)”、“产生(Generate)”、“迁移(Remove)”、“唯一(Unique)”、“翻转(Reverse)”、“旋转(Rotate)”、“任意洗牌(Random shuffle)”、“分区(Partitions)”。

说明,如果某个算法的后缀有_copy,表示它要把一个迭代器区间的内容拷贝到另一个

迭代器中。比如replace 有replace_copy 的变种。

 Group3 中的排序算法都有两个版本,一个用函数对象做比较,一个用operator< 做比较。比如算法“排序(sort)”有两个版本:

void sort(RandomAccessIterator first,RandomAccessIterator last);void sort(RandomAccessIterator first,RandomAccessIterator last, Compare comp) ;

Group3中还有算法“第N 个元素(Nthelement)”、“二分搜索(Binary Search)”、“合并

(Merge)”、“排好序的设置操作(Set operations on sorted structures)”、“堆操作(Heap Operations)

”、“最大最小(Minimum and Maximum)”、“词典比较(Lexicographical comparison)”、“置换产生器(Permutation generator)”。

 Group4 中包含些常用的数字算法,比如“聚集(Accumulate)”、“内部乘积(Innerproduct)”

、“局部和(Partialsum)”、“邻近不同(Adjacent difference)”。

 

5. 适配器(Adaptor)

适应器(Adaptor) 是提供接口映射的模板类(Adaptors are template classes that provide

interface mappings) 。适应器基于其他类来实现新的功能,成员函数可以被添加、隐藏,也可合并以得到新的功能。

5.1容器适配器

栈(Stack)

栈可以用向量(vector) 、线性表(list)或双向队列(deque)来实现:

stack<vector<int>> s1;

stack<list<int> > s2;

stack<deque<int>> s3;

其成员函数有“判空(empty)”、“尺寸(Size)”、“栈顶元素(top)”、“压栈(push)”、“弹栈(pop)”等。

队列(Queue)

队列可以用线性表(list)或双向队列(deque) 来实现(注意vector container 不能用来实现

queue ,因为vector没有成员函数pop_front!):

queue<list<int>> q1;

queue<deque<int>> q2;

其成员函数有“判空(empty)”、“尺寸(Size)”、“首元(front)”、“尾元(backt)”、“加入队列(push)”

、“弹出队列(pop)”等操作。

优先级队列(Priority Queue)

优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container 不能用来实

现queue,因为list 的迭代器不是任意存取iterator ,而pop中用到堆排序时是要求random

access iterator 的!):

priority_queue<vector<int>,less<int>> pq1; // 使用递增less<int>函数对象排序priority_queue<deque<int>,greater<int>> pq2; // 使用递减greater<int>函数对象排序

其成员函数有“判空(empty)”、“尺寸(Size)”、“栈顶元素(top)”、“压栈(push)”、“弹栈(pop)”等。

5.2迭代适配器

逆向迭代器(Reverse Iterator)

顾名思义,逆向迭代器的递增是朝反方向前进的。对于顺序容器(vector, list 和deque)

,其成员函数rbegin()和rend()都返回了相应的逆向迭代器:

list<int> l;for (int i = 1; i < 5; i++) l.push_back(i);copy(l.rbegin(), l.rend(),ostream_iterator<int> (cout, “ “));

输出结果是:4 3 2 1

插入迭代器(Insert Iterator)

插入迭代器简化了向容器中插入元素的工作。插入迭代器指定了向容器中插入元素的位

置。STL中有三种插入迭代器:

·向后插入(back_insert_iterator) ,在容器尾部插入

·向前插入(front_insert_iterator) ,在容器头部插入

·插入(insert_iterator) ,在容器中任一位置

STL中提供了三个函数分别构造相应的插入迭代器:

·back_inserter

·front_inserter

·inserter

原始存储迭代器(Raw Storage Iterator)

该迭代器允许算法将其结果保存进没有初始化的内存中。

5.3函数适配器

否认者(Negator)

有两个否认者not1 和not2 分别是一元和二元函数,它们分别使用一元和二元的谓词

(Predicate) ,返回的是谓词的结果的取反。

    绑定者(Binder)

STL中有两个绑定者函数bind1st 和bind2nd ,可以将一些值限定在指定区间中。

函数指针的适配器(Adaptors for pointers tofunction)

 STL 中的算法和容器一般都需要函数对象(function object) 作为参数。如果想用到常用的C++函数时,可以用ptr_fun 把普通函数转换成函数对象。比如ptr_fun(strcmp) 就把常用的串比较函数strcmp 包装成一个函数对象,就可用在STL 算法和容器中了。

 


6 其余的STL部件

(1)分配算符和内存处理

可移植性的一个主要问题是能够把有关内存模型的信息封装起来。这些信息有:

·指针类型

·指针差异的类型(ptrdiff_t)

·内存模型中对象尺寸的类型(size_t)

·内存分配和回收原语

STL中提供的分配算符(allocator) 对象封装了以上信息。STL 中的容器(container)都有一个alllocator 参数,这样容器就不用关心内存模型信息了。

STL中提供了缺省的allocator 对象,各家编译器也提供其产品支持的不同的内存模型

allocator 。对每一种内存模型都要提供以下几个模板函数:

·allocate :分配缓冲区

·deallocate :回收缓冲区

·construct :通过调用合适的拷贝构造函数将结果直接放入没有初始化的内存中

·destroy:调用指定指针的析构函数

(2) 各部件如何协同工作

STL的容器(contianer) 用来存储任意类型的对象。容器需要分配算符(allocator) 为参数。

分配算符(allocator) 是能够封装所使用内存模型信息的对象,它提供内存原语以对内存进行统一的存取。每种内存模型都有自己特定的allocator 。Container 使用allocator完成对内存的操作,内存模型的改变只影响allocator ,而不会(在代码一级)影响contianer 对象。

算法(algorithm) 是计算顺序。两个算法不同在其本身的计算上,而非读取输入数据和写出输出数据的方法上。STL 为算法提供了统一的数据存取机制 ---迭代器(iterator)。不同的迭代器提供不同存取方式。

函数对象(function object) 用在和算法的结合中,用以扩展算法的效用。

适应器(adaptor) 是接口映射,它们在基本或已有的部件上实现新的对象,以提供不同或扩展的能力。

在设计STL 时,不同部件间的接口都定义的尽可能少。STL 的目的在于:

·简化应用程序的设计

·减少要写的代码函数

·增加可理解度和可维护性

·提供基本的质量保证

实际上STL 中有的功能在MFC 中都有替代品。例如在MFC 中有CArray等。不过,在你只能使用标准C/C++ 如在Unix 或Linux环境下要使用这些数据结构,使用STL 是一个很好的选择。STL 最大的特点是它使用的算法非常可靠且效率很高,例如其中的二叉树算法就很经典。不过看 STL 源代码还是有点困难的,需要下点功夫。不过如果只是要使用可以不看源代码,使用起来很简单的。

0 0