标准模板库(STL)介绍

标准模板库,又称STL,是一个容器(container)类、

算法(algorithms)、迭代器(iterators)的C++库;它

提供了许多科学计算的基本算法和数据结构。STL

是一个泛型(generic)库,意味着他的组件大量地参

数化:在STL中几乎每个组件都是模板(template)。

在用STL之前,你必须理解C++中如何使用模板。

                      <script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

 

容器(Containers)和算法(algorithms)

和许多类库一样,STL包含容器(container)类:可以容纳其他对象的类。STL包含 vector, list, deque, set, multiset, map, multimap, hash_set, hash_multiset, hash_maphash_multimap 类。这些类每个都是模板(template),能被实例化来包含各种类型的对象。例如,你可以用 vector ,就象常规的C语言中的数组,额外地,vector 能消除手工管理动态内存分配的杂事。

      vector
 
  v(3);            // 定义一个有三个元素的vector类
      v[0] = 7;
      v[1] = v[0] + 3;
      v[2] = v[0] + v[1];          // v[0] == 7, v[1] == 10, v[2] == 17  

STL也包含了大量的算法(algorithm),用来巧妙地处理存储于容器中的数据。例如,你能用 reverse 算法颠倒 vector 中元素的顺序。

      reverse(v.begin(), v.end()); // v[0] == 17, v[1] == 10, v[2] == 7

调用 reverse,要注意两点。第一、它是个全局函数,而不是成员函数。第二、它需要两个参数而不是一个:它操作一定区间内的元素,而不是操作容器。在这个特例中,区间范围恰巧是整个容器 v。

这两点的实事的动机是一样的: reverse,其他STL算法也类似,弱化和STL容器类的联系。这意味着 reverse 不仅能用于颠倒 vector 中的元素次序,也能颠倒list甚至C数组中元素的次序。下面的程序是合法的。

      double A[6] = { 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 };
      reverse(A, A + 6);
      for (int i = 0; i < 6; ++i)
        cout << "A[" << i << "] = " << A[i];

正如倒序 vector 的例子一样,这个例子也用一个区间:用于倒序的第一个参数是指向区间开始位置的指针,第二个参数指向越过区间末尾元素的位置。这个区间表示为 [A, A + 6); 不对称符号预示两个端点含义不同,第一个是区间的开始,第二个是区间末尾的下一个位置。

迭代器(Iterators)

在倒序C数组的例子中,参数类型无疑是 double* 类型。假如倒序 vector 或 list,倒序参数类型是什么?换句话说,reverse 声明它的参数到底是什么,v.begin() 和 v.end() 返回的到底是什么?

答案是 reverse 的参数是迭代器(iterator),是一个泛化的指针。指针自己也是 iterator,这就是为什么可以倒序C数组的元素。同样,vector声明了嵌套的类型 iterator 和 const_iterator。在上例中,v.begin() 和 v.end() 返回的类型是 vector ::iterator。也有一些迭代器,诸如 istream_iteratorostream_iterator, 根本不能和任何容器关联。

iterator 是一种弱化容器和算法之间的关联的机制:算法是模板,并且被 iterator 的类型参数化,因此不受限于单一的容器类型。例如,考虑如何写个算法完成一定区间的线性查找。下面是STL的 find  算法:

      template 
 
 
      InputIterator find(InputIterator first, InputIterator last, const T& value) {
          while (first != last && *first != value) ++first;
          return first;
      }

find 有三个参数:两个 iterators 定义一个区间,第三个是从所定区间要查找的值。它检查在区间[first, last)内的每个 iterator,从头到尾处理,停止于当其发现一个 iterator 所指向的值等于 value,或当其到达区间的结尾。
 

first 和 last 定义为类型 InputIterator,InputIterator 是一个模板型参数。也就是说,实际上被调用的 InputIterator 不是真实的类型:当你调用 find,编译器用实参类型来取代形参 InputIterator 和 T。 假如 find 的前两个参数类型是 int*,第三个参数类型是 int,那么实际上你调用的是下面这个函数:

      int* find(int* first, int* last, const int& value) {
          while (first != last && *first != value) ++first;
          return first;
      }

约束(Concepts)和类属实参(Modeling)

对于模板函数,不仅仅是STL算法,一个非常重要的问题是,用什么样的参数类型能够匹配形式模板参数?很明确,例如,int* 和 double* 可以取代 find 的形式模板参数。而 int 和 double就不行:find用表达式 *first,反引用操作符使得类型为 int 或 double 的对象没有意义。基本答案是,find 隐含地定义了对类型的要求,使其可以被满足这些要求的任何类型实例化。凡是取代InputIterator 的类型必须支持一定的操作:两个对象必须能比较大小,对象必须能够进行递增操作,对象反引用后必须能够获得其指向的对象,等等。

find 不是唯一的有如此要求的STL算法,对于 for_eachcount 和其他算法的参数,也必须满足同样的要求。这些要求非常重要,我们给它们一个名:我们称呼这样一组类型需要一个约束(concept),我们称这个特定的约束为 Input Iterator. 假如一个类型满足所有要求,我们说它遵守约束,或它是约束的类属实参(model)。我们说 int* 是 Input Iterator 的类属实参,因为 int* 提供 Input Iterator 所要求的所有操作。

约束不是C++语言的一部分;没有途径在程序中定义一个约束,或定义一个特定的类型是约束的类属实参。然而,约束是STL极其重要的一部分。用约束实现了写出接口分离的程序成为可能: find 的作者仅仅考虑约束 Input Iterator 指定的接口的实现,而不是实现和那个约束一致的任何可能的类型。同样地,假如你想用 find,你仅仅需要保证你传递的参数是 Input Iterator 的类属实参就可。这是为什么 findreverse 能用于 lists, vectors, C arrays, 和许多其他类型的原因:按照约束编程,而不是按照特定的类型,使得重用软件组件和组件组合成为可能。

Refinement

Input Iterator 实际上是想当弱的约束(concept):就是说,它只有少数几个要求。一个 Input Iterator 必须支持指针运算的子集(它必须能用前缀或后缀 operator++ 来递增 Input Iterator ), 但无需支持指针的所有算法。这对于 find 是足够的,但一些其他的算法需要其参数满足其他的条件。例如, Reverse 参数必须能够像递增一样可以递减;因为它使用用表达 --last。 用约束的术语来讲,我们说 reverse 的参数必须是 Bidirectional Iterator ,而不是 Input Iterator

Bidirectional Iterator 约束和 Input Iterator 约束很相近:他只是简单地增加了一点另外的条件。Bidirectional Iterator 类属实参是 Input Iterator 类属实参的子集:每一个 Bidirectional Iterator 的类属实参的数据类型,那它也是 Input Iterator 类属实参。例如,int* 既是 Bidirectional Iterator 的类属实参,也是 Input Iterator 的类属实参,但 istream_iterator 仅仅是 Input Iterator 的类属实参:它不符合更严格的 Bidirectional Iterator 的要求。

我们形容 Input IteratorBidirectional Iterator 的关系,通常说 Bidirectional IteratorInput Iteratorrefinement 。约束的 Refinement 非常像 C++ 类的继承;我们用一个不同的词,而不叫“继承(inheritance)”的主要原因是强调 refinement 适用于约束而不是实际类型。

实际上还有三种迭代器约束,加上我们已经讨论的两种:五种迭代器约束是 Output Iterator, Input Iterator, Forward Iterator, Bidirectional Iterator, 和 Random Access Iterator; Forward IteratorInput Iterator的 refinement,Bidirectional IteratorForward Iterator 的 refinement,Random Access IteratorBidirectional Iterator的 refinement。 (Output Iterator 和其他四种约束有关,但是它不是 refinement 层次上的关系:它不是任何其他迭代器约束的refinement ,其它迭代器约束也不是它的refinement 。)  Iterator 浏览 有更全面的迭代器的信息。

像迭代器一样,容器类Container classes,被组织到约束体系中. 所有容器是 Container的模型; 更精确的约束,诸如 SequenceAssociative Container 描述特定类型的容器。

STL的其他部分

假如你理解了算法、迭代器和容器,你就理解了有关STL的绝大部分事情。可是,STL还包括其他几个组件。

首先,STL包含几个工具(utilities): :非常基本的约束和函数,被用于库的许多不同部分。例如,Assignable 约束, 描述用于赋值操作符和拷贝构造的类型;几乎所有的STL类是 Assignable 的类属实参,几乎所有的STL算法要求其参数是 Assignable 的类属实参。

第二,STL包含一些底层机制用来分配和释放内存。分配器( Allocators )是专门用于这种目的,在大多数情况你的确可以忽略它。

最后,STL包含了大量的函数对象( function objects),也称为仿函数( functors)。正如迭代器泛化指针,函数对象用来泛化函数:一个函数对象是能像函数语法一样调用的任何东西。 这里对函数对象有几个不同的约束,包括 Unary Function (函数对象接受一个参数,例如 f(x)) 和 Binary Function (函数对象接受两个参数,例如 f(x,y))。 函数对象是泛型编程的重要部分,他如同对象类型的抽象一样作用于操作。

原著:《Standard Template Library Programmer's Guide》

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值