C++中STL用法汇总

发布时间 2023-09-15 12:06:56作者: ImreW

1 什么是STL?

STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。

STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但这种分离确实使得STL变得非常通用。例如,由于STL的sort()函数是完全通用的,你可以用它来操作几乎任何数据集合,包括链表,容器和数组;

STL另一个重要特性是它不是面向对象的。为了具有足够通用性,STL主要依赖于模板而不是封装,继承和虚函数(多态性)——OOP的三个要素。你在STL中找不到任何明显的类继承关系。这好像是一种倒退,但这正好是使得STL的组件具有广泛通用性的底层特征。另外,由于STL是基于模板,内联函数的使用使得生成的代码短小高效;

从逻辑层次来看,在STL中体现了泛型化程序设计的思想,引入了诸多新的名词,比如像需求(requirements),概念(concept),模型(model),容器(container),算法(algorithmn),迭代子(iterator)等。与OOP(object-oriented programming)中的多态(polymorphism)一样,泛型也是一种软件的复用技术;

从实现层次看,整个STL是以一种类型参数化的方式实现的,这种方式基于一个在早先C++标准中没有出现的语言特性--模板(template)。

2 STL内容介绍

STL中六大组件:

  • 容器(Container),是一种数据结构,如list,vector,和deques ,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;
  • 迭代器(Iterator),提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象;
  • 算法(Algorithm),是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用;
  • 仿函数(Functor
  • 适配器(Adaptor
  • 分配器(Allocator

2.1 容器

STL中的容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。

(1)序列式容器(Sequence containers),每个元素都有固定位置--取决于插入时机和地点,和元素值无关,vector、deque、list;

  • Vector:将元素置于一个动态数组中加以管理,可以随机存取元素(用索引直接存取),数组尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时;
  • Deque:是“double-ended queue”的缩写,可以随机存取元素(用索引直接存取),数组头部和尾部添加或移除元素都非常快速。但是在中部或头部安插元素比较费时;
  • List:双向链表,不提供随机存取(按顺序走到需存取的元素,O(n)),在任何位置上执行插入或删除动作都非常迅速,内部只需调整一下指针;

(2)关联式容器(Associated containers),元素位置取决于特定的排序准则,和插入顺序无关,set、multiset、map、multimap等。

  • Set/Multiset:内部的元素依据其值自动排序,Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素,内部由二叉树实现,便于查找;
  • Map/Multimap:Map的元素是成对的键值/实值,内部的元素依据其值自动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现,便于查找;

容器类自动申请和释放内存,无需new和delete操作。

2.2 STL迭代器

Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。或者这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。

迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型:iterator、const_iterator、reverse_iterator和const_reverse_iterator.

2.3 算法

函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比使用整型作为它的参数类性要高。而C++通过模板的机制允许推迟对某些类型的选择,直到真正想使用模板或者说对模板进行特化的时候,STL就利用了这一点提供了相当多的有用算法。它是在一个有效的框架中完成这些算法的——你可以将所有的类型划分为少数的几类,然后就可以在模版的参数中使用一种类型替换掉同一种类中的其他类型。

STL提供了大约100个实现算法的模版函数,比如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以你所指定的规则对序列进行稳定性排序等等。只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能并大大地提升效率。

算法部分主要由头文件<algorithm><numeric><functional>组成。

  • <algorithm>是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。
  • <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
  • <functional>中则定义了一些模板类,用以声明函数对象。

STL中算法大致分为四类:

  1. 非可变序列算法:指不直接修改其所操作的容器内容的算法。
  2. 可变序列算法:指可以修改它们所操作的容器内容的算法。
  3. 排序算法:对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作。
  4. 数值算法:对容器内容进行数值计算。

以下对所有算法进行细致分类并标明功能:

<一>查找算法(13个):判断容器中是否包含某个值

  1. adjacent_find:   在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的ForwardIterator。否则返回last。重载版本使用输入的二元操作符代替相等的判断。
  2. binary_search: 在有序序列中查找value,找到返回true。重载的版本实用指定的比较函数对象或函数指针来判断相等。
  3. count:                利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。
  4. count_if:            利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。
  5. equal_range:     功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。
  6. find:                   利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的一个InputIterator。
  7. find_end:          在指定范围内查找"由输入的另外一对iterator标志的第二个序列"的最后一次出现。找到则返回最后一对的第一个ForwardIterator,否则返回输入的"另外一对"的第一个ForwardIterator。重载版本使用用户输入的操作符代替等于操作。
  8. find_first_of:     在指定范围内查找"由输入的另外一对iterator标志的第二个序列"中任意一个元素的第一次出现。重载版本中使用了用户自定义操作符。
  9. find_if:               使用输入的函数代替等于操作符执行find。
  10. lower_bound:   返回一个ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器顺序的第一个位置。重载函数使用自定义比较操作。
  11. upper_bound:  返回一个ForwardIterator,指向在有序序列范围内插入value而不破坏容器顺序的最后一个位置,该位置标志一个大于value的值。重载函数使用自定义比较操作。
  12. search:              给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作。
  13. search_n:          在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作。

<二>排序和通用算法(14个):提供元素排序策略

  1. inplace_merge:      合并两个有序序列,结果序列覆盖两端范围。重载版本使用输入的操作进行排序。
  2. merge:                    合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较。
  3. nth_element:          将范围内的序列重新排序,使所有小于第n个元素的元素都出现在它前面,而大于它的都出现在后面。重                                    载版本使用自定义的比较操作。
  4. partial_sort:            对序列做部分排序,被排序元素个数正好可以被放到范围内。重载版本使用自定义的比较操作。
  5. partial_sort_copy: 与partial_sort类似,不过将经过排序的序列复制到另一个容器。
  6. partition:                 对指定范围内元素重新排序,使用输入的函数,把结果为true的元素放在结果为false的元素之前。
  7. random_shuffle:    对指定范围内的元素随机调整次序。重载版本输入一个随机数产生操作。
  8. reverse:                  将指定范围内元素重新反序排序。
  9. reverse_copy:        与reverse类似,不过将结果写入另一个容器。
  10. rotate:                     将指定范围内元素移到容器末尾,由middle指向的元素成为容器第一个元素。
  11. rotate_copy:           与rotate类似,不过将结果写入另一个容器。
  12. sort:                         以升序重新排列指定范围内的元素。重载版本使用自定义的比较操作。
  13. stable_sort:            与sort类似,不过保留相等元素之间的顺序关系。
  14. stable_partition:    与partition类似,不过不保证保留容器中的相对顺序。

<三>删除和替换算法(15个)