模板的引入:如图,如果编写了一个两个int 型相加的 add() 函数,那么这个函数就只能实现 int 型加法,对 float 等其他类型无法实现,要实现这些其他类型的加法就要重新编写add()函数。结果导致这样的情况出现:即使拥有同一个函数名,相同的函数体,却因为参数类型和返回值类型不一样,仍然不得不为每一个函数编写一组函数体完全相同的代码。如上图就出现了3个不同的函数,即使它们是二元加法的重载函数。如果能从这些函数中提炼出一个通用函数,而它又适用于多种不同类型的数据,就会使代码的重用率大大提高,C++通过模板来解决这样的问题。
模板(Template)是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
模版是一种解决方案,其目的就是为了减少重复代码,提供参数化容器类和通用的算法(函数),从而真正地实现代码可重用性,能够让程序员编写与类型无关的代码,解放C++程序员生产力。
模板常有两种形式:函数模板和类模板。而把函数模板的具体化称为模板函数,把类模板的具体化成为模板类。
模板可以认为是类的类,我们知道一个类可以实例化为一个对象,那么类其实可以认为是一个模板的一份实例,我们可以让一个模板产生多份类实例,只需要简单的指定不同的模板参数,这样可以进一步提高代码的抽象性。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
STL,即 Standard Template Library,中文译为标准模板库。其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。
从根本上说,STL 是一些容器、算法和其他一些组件的集合。所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的,因此可以说,STL 基本上达到了各种存储方法和相关算法的高度优化。注意,这里提到的容器,本质上就是封装有数据结构的模板类,例如 list、vector、set、map 等。
STL 最初由惠普实验室开发,于 1998 年被定为国际标准,正式成为 C++ 程序库的重要组成部分。值得一提的是,如今 STL 已完全被内置到支持 C++ 的编译器中,无需额外安装,这可能也是 STL 被广泛使用的原因之一。
STL 就位于各个 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)。
STL 中加入了一个封装内存模式信息的抽象模块,也就是 STL 中的 allocator(内存分配器),它使 STL 的大部分实现都可以独立于具体的内存模式,从而独立于具体平台。
通常认为,STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 大组件组成,其中后面的 4 个组件可以认为是为前 2个组件服务的,C++ STL基本组成为:6大组件+13个头文件
1)容器(Container):是以模板类的方法提供的各种数据结构。从实现的角度看,STL容器是一些封装数据结构的class template。例如 vector 向量容器、list 列表容器等。为了访问容器中的数据,可以使用由容器类输出的迭代器。
2)算法(Algorithm):是用来操作容器中的数据的模板函数。从实现的角度看,STL算法是一种function template。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。STL 提供了非常多(大约 100 个)的数据结构算法,它们都被设计成一个个的模板函数,这些算法在 std 命名空间中定义。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。
3)迭代器(Iterator):提供了访问容器中对象的方法。在C++ STL 中,对容器中数据的读和写,是通过迭代器完成的。它扮演着容器和算法之间的胶合剂,是所谓的“泛型指针”。从实现的角度来看,迭代器是一种将“operator, operator->, operator++, operator--”等指针相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器,--是的,只有容器的设计者才知道如何遍历自己的元素。C++的原生指针也是一种迭代器。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象。
4)仿函数(Functor):行为类似函数,可作为算法的某种策略(policy)。从实现的角度看,仿函数是一种重载了operator()的class 或 class template。一般函数指针可视为狭义的仿函数。例如,如果一个类将 () 运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象(又称对象函数)。
5)配接器(Adapter):一种用来修饰容器、仿函数或迭代器接口的东西。例如,STL提供的queue和stack,虽然看似容器,其实只能算是一种容器配接器,因为它们的底部完全借助 deque,所有的的操作都由底层的deque供应。改变functor接口者,称为 function adapter;改变Container接口者,称为 Container adapter;改变Iterator接口者,称为 Iterator adapter。即,配接器可以使一个类的接口(模板的参数)适配成用户指定的形式,从而让原本不能在一起工作的两个类工作在一起。
6)配置器(Allocator):为容器类模板提供自定义的内存配置与管理功能。从实现的角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。由于往往只有高级用户才有改变内存分配策略的需求,因此内存分配器对于一般用户来说,并不常用。
在惠普实验室最初发行的版本中,STL 被组织成 48 个头文件;但在 C++ 标准中,它们被重新组织为 13 个头文件,如表 1 所示。按照 C++ 标准库的规定,所有标准头文件都不再有扩展名。以
STL中的容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。
1 )序列式容器(Sequence containers):
每个元素都有固定位置-取决于插入时机和地点,和元素值无关,vector、deque、list;
double-ended queue
的缩写,可以随机存取元素(用索引直接存取),数组头部和尾部添加或移除元素都非常快速。但是在中部或头部安插元素比较费时;2) 关联式容器(Associated containers)
元素位置取决于特定的排序准则,和插入顺序无关,set、multiset、map、multimap;
容器类自动申请和释放内存,无需new和delete操作。
Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。或者这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。
迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型:iterator、const_iterator、reverse_iterator和const_reverse_iterator。
STL提供了大约100个实现算法的模版函数,比如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以你所指定的规则对序列进行稳定性排序等等。只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能并大大地提升效率。
算法部分主要由头文件
STL中算法大致分为四类:
1)非可变序列算法:指不直接修改其所操作的容器内容的算法。
2)可变序列算法:指可以修改它们所操作的容器内容的算法。
3)排序算法:对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作。
4)数值算法:对容器内容进行数值计算。
以下对所有算法进行细致分类并标明功能:
【1】查找算法(13个):判断容器中是否包含某个值
【2】排序和通用算法(14个):提供元素排序策略
【3】删除和替换算法(15个)
【4】排列组合算法(2个):提供计算给定集合按一定顺序的所有可能排列组合
【5】算术算法(4个)
【6】生成和异变算法(6个)
【7】关系算法(8个)
【8】集合算法(4个)
【9】堆算法(4个)
仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。 有些功能的的代码,会在不同的成员函数中用到,想复用这些代码: 1、公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。 2、仿函数,写一个简单类,除了那些维护一个类的成员函数外,就只是实现一个operator(),在类实例化时,就将要用的,非参数的元素传入类中。
算术类仿函数 | 关系运算类仿函数 | 逻辑运算仿函数 |
---|---|---|
加:plus |
等于:equal_to |
逻辑与:logical_and |
减:minus |
不等于:not_equal_to |
逻辑或:logical_or |
乘:multiplies |
大于:greater |
逻辑否:logical_no |
除:divides |
大于等于:greater_equal |
|
模取:modulus |
小于:less |
|
否定:negate |
小于等于:less_equal |
STL提供了三个容器适配器:queue、priority_queue、stack。这些适配器都是包装了vector、list、deque中某个顺序容器的包装器。注意:适配器没有提供迭代器,也不能同时插入或删除多个元素。......
以上内容来源网络 经过整理而成
参考链接:
https://www.cnblogs.com/biyeymyhjob/archive/2012/07/22/2603525.html
String类:https://www.cnblogs.com/lanxiang/p/11252404.html
http://c.biancheng.net/stl/algorithms/