标准模板库(standard template library,STL)是C++十分强大的库,其提供了一组表示容器、蝶代替、函数对象和算法的模板。STL实际上是使用泛型编程(generic programming)编写的,其建立在一些概念的抽象之上,如容器、迭代器、分配器等。OOP和泛型编程的理念不同,OOP关注编程的数据表示,泛型编程关注的则是算法,拥有更高的抽象水平。
以下介绍STL中的泛型编程的理念、容器类、迭代器、算法以及函数对象。
目录
泛型编程
迭代器
函数对象
容器类
序列
关联容器
无序关联容器
算法
为了能够让泛型编程能够适用于许多类型,泛型编程主要通过类模板和函数模板以及各种抽象概念实现。抽象概念包括分配器、迭代器、容器等。这些抽象概念有对应具体的类,如allocator类、iteration类等。
迭代器是一种概念的抽象,也有具体的实现(即iterator类)。不同算法对迭代器的要求不同,根据不同要求(读写数据、遍历方向、随机访问),其分为5类。每种迭代器均能够满足前文提及的概念,但也具有各自不同的概念。
实际上,这5类迭代器具有一定继承关系:正向迭代器继承了输入迭代器、输出迭代器的所有特点;双向迭代器继承了正向迭代器的所有特点;随机迭代器继承了双向迭代器的所有特点。
/*声明:
ostream_iterator<被发送给输出流的类型,输出流的类型> 迭代器对象名(输出流);//构造函数1
ostream_iterator<被发送给输出流的类型,输出流的类型> 迭代器对象名(输出流,数据项的分隔符);//构造函数2
*/
//示例
ostream_iterator objOI(cout);
/*声明:
istream_iterator<输入流读取的类型,输入流的类型> 迭代器对象名(输入流);//构造函数1
istream_iterator<输入流读取的类型,输入流的类型> 迭代器对象名(输入流,数据项的分隔符);//构造函数2
*/
//示例
istream_iterator objII(cin);
/*构造函数
back_insert_iterator<容器类型> 迭代器名(类型对应的容器对象);
front_insert_iterator<容器类型> 迭代器名(类型对应的容器对象);
insert_iterator<容器类型> 迭代器名(类型对应的容器对象,插入位置);
//插入位置实际上也是个迭代器
*/
//示例
#include
#include
#include
using namespace std;
vector v(4);
back_insert_iterator> objBII(v);
insert_iterator> objII(v,v.begin());
运算符 | 函数符 |
+ | plus |
- | minus |
* | multiplies |
/ | divides |
% | modulus |
-(取负) | negate |
== | equal_to |
!= | not_equal_to |
> | greater |
< | less |
>= | greater_equal |
<= | less_equal |
&& | logical_and |
|| | logical_or |
! | logical_not |
/*
//调用于生成函数符对象:
函数符名<类型> 对象名;
//调用函数
对象名(左值,右值)
//传入容器的构造函数作为比较对象时
//只需传入函数符即可,不需要加上()
*/
//示例
void main()
{
plus add;
int ans=add(1,2);
cout<
class Greater//类函数符
{
public:
Greater(const int & val_a,const int & val_b):a(val_a),b(val_b){};
~Greater();
bool operator()(){return a>b;}//重载()运算符
private:
int a;
int b;
};
void main()
{
set objSet;//传入Greater作为比较对象
}
上文的代码段中的Greater是针对int类型的,其实也可以像预定义的函数符一样,将其定义为一个类模板,这样其普适性会更强。容器是按照一定组织结构储存数值的对象,其实就是一系列数据结构的实现(或者用STL的术语来说,模型)。STL中有许多强大的容器类,主要有几大类:
容器类的基本特征(或者说基本成员)主要有:(X表示序列类型,t为类型T的值,a、b为容器对象,n表示整数,p、q、i、t为迭代器)
表达式 | 返回类型 | 说明 | 时间复杂度 |
X::iterator | - | 满足正向迭代器的任何迭代器 | 编译时间 |
X::value_type | T | T的类型 | 编译时间 |
X u | 创建一个空容器对象u | 固定时间 | |
X() | 创建一个匿名空容器对象 | 固定时间 | |
X u(a) | 迭代器 | 调用复制构造函数,使用容器对象a创建容器对象u | 线性时间 |
X u=a | void | 同X u(a) | 线性时间 |
a.begin() | (常量)迭代器 | 返回指向容器第一个元素的迭代器 | 固定时间 |
a.end() | 迭代器 | 返回指向容器超尾(即最后一个元素以后)的迭代器 | 固定时间 |
a.size() | 整数 | 返回容器元素数 | 固定时间 |
a.swap(b) | void | 交换a和b的内容 | 固定时间 |
a==b | bool | 如果a、b长度相同,且a、b中的每个元素都相同,则返回真 | 线性时间 |
a!=b | bool | 返回!(a==b) | 线性时间 |
编译时间指操作将在编译时执行,执行时间为0;固定时间、线性时间均表示操作在运行时执行,固定时间但独立于对象的元素数,线性时间则与元素数成正比。
以下主要从容器类的特点、声明、方法。介绍各容器类,可以联系数据结构的内容以加深理解。使用各容器前,记得包含它们各自的头文件,头文件名一般与它们的名称相同。
序列是对容器概念的改进,在容器的概念之上,要求:
①迭代器至少应为正向迭代器
②元素应按严格的线性顺序排列
下表总结了每种序列的基本成员的相同部分:(X表示序列类型,t为类型T的值,a为序列对象,n表示整数,p、q、i、t为迭代器)
成员表达式 | 返回类型 | 说明 |
X a(n,t) | - | 声明一个由n个t组成的序列对象a |
X(n,t) | 声明一个由n个t组成的匿名序列 | |
X a(i,t) | 声明一个由区间[i,j)之间的内容组成的序列对象a | |
X(i,t) | 声明一个由区间[i,j)之间的内容组成的匿名序列 | |
a.insert(p,t) | 迭代器 | 将t插入到p前面 |
a.insert(p,n,t) | void | 将n个t插入到p前面 |
a.insert(p,i,j) | void | 将区间[i,j)的内容插入到p前面 |
a.erase(p) | 迭代器 | 删除p |
a.erase(p,q) | 迭代器 | 删除区间[p,q)的内容 |
a.clear() | void | 清空所有内容 |
以下作各序列的简介。各类的方法可以查看相关STL使用目录(例如C++ primer plus第六版的附录G)。
函数 | 说明 | 时间复杂度 |
void merge(list |
将对象与相同类型的链表x合并 | 线性时间 |
void remove(const T & val) | 删除val的所有实例 | 线性时间 |
void sort() | 使用<运算符排序 | NlogN |
void splice(iterator pos, list |
将相同类型链表x插入到p位置前,并且x将被置空 | 固定时间 |
void unique() | 将连续相同元素压缩为单个元素 | 线性时间 |
函数 | 返回类型 | 说明 |
void rend() | reverse_iterator | 返回指向反转序列的末尾元素(即正序第一个元素)的迭代器 |
void rbegin() | reverse_iterator | 返回指向反转序列第一个元素(即正序最后一个元素)的迭代器 |
函数 | 说明 |
bool empty() const | 查看队列是否为空 |
int size() const | 查看队列元素个数 |
T & front() | 返回头部元素的引用 |
T & back() | 返回尾部元素的引用 |
void push(T &x) | 将元素x压入队尾 |
void pop() | 删除头部元素 |
/*
priority_queue<元素类型> 堆对象名;//构造函数1
priority_queue<元素类型> 堆对象名(greater<元素类型>)//构造函数2
*/
greater<>是预定义的函数对象。函数 | 说明 |
bool empty() const | 查看队列是否为空,若为空返回true,非空则返回false |
int size() const | 查看栈元素个数 |
T & top() | 返回栈顶元素的引用 |
void push(const T &x) | 将元素x压入栈顶 |
void pop() | 删除栈顶元素 |
关联容器是对容器概念的改进,其将值与键关联在一起,并使用键查找值。关联容器都提供了对元素的快速访问(其所需时间为固定时间)。关联容器一般通过树实现。
函数 | 说明 |
X::key_type | 键Key,键类型 |
X::key_compare | 键的比较对象Compare,默认为less |
X::value_compare | 值的比较对象Compare,二元谓词类型 |
函数 | 说明 |
set |
创建键值类型为T的set对象A |
set |
创建键值类型为T的set对象A,其内容为对象B的区间[begin, end)的内容,并使用函数对象c作为比较对象,c默认为less |
set_union(i, j, p, q, objOI) | 并集,将对象A区间[i,j)、对象B区间[p,q)的内容合并并输出到objOI中。objOI需要是输出迭代器,不能是某个容器对象的begin(),因为begin()返回的是常量迭代器,此时可以用insert_iterator。 |
set_difference(i, j, p, q, objOI) | 差集,找出对象A区间[i,j)、对象B区间[p,q)相差的内容,并输出到objOI中。 |
set_intersection(i, j, p, q, objOI) | 交集,找出对象A区间[i,j)、对象B区间[p,q)的相交内容,并输出到objOI中。 |
A.find(k) | 返回一个迭代器,该迭代器指向键与 k 相同的元素;如果没有找到这样的元素,则返回 a.end() |
A.lower_bound(k) | 返回一个迭代器,该迭代器指向第一个键不小于 k 的元素 |
A.upper_bound(k) | 返回一个迭代器,该迭代器指向第一个键不大于 k 的元素 |
A.insert(t) | 插入一个值为t的键 |
A.insert(p, q) | 插入B集合中区间[p,q)的值 |
函数 | 说明 |
multiset |
创建键值类型为T的set对象A,比较对象c默认为less |
multiset |
创建键值类型为T的set对象A,其内容为对象B的区间[begin, end)的内容,并使用函数对象c作为比较对象,c默认为less |
A.find(k) | 返回一个迭代器,该迭代器指向键与 k 相同的元素;如果没有找到这样的元素,则返回 a.end() |
A.lower_bound(k) | 返回一个迭代器,该迭代器指向第一个键不小于 k 的元素 |
A.upper_bound(k) | 返回一个迭代器,该迭代器指向第一个键不大于 k 的元素 |
A.insert(t) | 插入一个值为t的键 |
A.insert(p, q) | 插入B集合中区间[p,q)的值 |
#include
#include
using namespace std;
int main()
{
int arrInt[]={12,2,3,5,6};
int array[] = { 0, 2, 1, 4, 3, 6, 5, 7, 8, 9, 0, 7, 6};
set objSET(&arrInt[0],&arrInt[4]);
cout<<*objSET.find(2)< ms(array, array + sizeof(array) / sizeof(array[0]));
cout <<*ms.find(6)<
map、multimap都位于头文件map中。它们都是按照键排序的,也可以反转。
map中的键是唯一的,不能存储多个相同值,键、值互相关联,且类型可以不同。multimap中可以储存多个重复键,键、值互相关联,且类型可以不同。
· map和multimap共有的成员
函数 | 说明 |
X::key_type | 表示键Key,类型 |
X::key_compare | 表示键的比较对象Compare,默认为less |
X::value_compare | 二元谓词类型,与 set 和 multiset 的 key_compare 相同,为 map 或 multimap 容器中的 pair |
X::mapped_compare | 表示值类型,即T |
函数 | 说明 |
map |
创建键类型为KT、值类型为VT的set对象A,比较对象c默认值为less |
map |
创建键值类型为T的set对象A,其内容为对象B的区间[p, q)的内容,并使用函数对象c作为比较对象,c默认为less |
A.find(k) | 返回一个迭代器,该迭代器指向键与 k 相同的元素;如果没有找到这样的元素,则返回 a.end()。 map |
A[k] | 返回一个指向与键 k 关联的值的引用(map特有的数组表示法) |
A.lower_bound(k) | 返回一个map |
A.upper_bound(k) | 返回一个迭代器,该迭代器指向第一个键不大于 k 的元素 |
A.insert(pair |
插入一个键值对为(v,k)的pair对象 |
A.insert(p, q) | 插入B集合中区间[p,q)的值 |
函数 | 说明 |
multimap Alloc=allocator |
创建键类型为KT、值类型为VT的multimap对象A,比较对象c默认为less |
multimap |
创建键值类型为T的set对象A,其内容为对象B的区间[begin, end)的内容,并使用函数对象c作为比较对象,c默认为less |
A.find(k) | 返回一个迭代器,该迭代器指向键与 k 相同的元素;如果没有找到这样的元素,则返回 a.end() |
A.lower_bound(k) | 返回一个迭代器,该迭代器指向第一个键不小于 k 的元素 |
A.upper_bound(k) | 返回一个迭代器,该迭代器指向第一个键不大于 k 的元素 |
A.insert(pair |
插入一个键值对为(v,k)的pair对象 |
A.insert(p, q) | 插入B集合中区间[p,q)的值 |
#include
#include
using namespace std;
int main()
{
int arrInt[]={12,2,3,5,6};
int array[] = { 0, 2, 1, 4, 3, 6, 5, 7, 8, 9, 0, 7, 6};
map objmap;
multimapobjMTmap;
int arrIntSize=sizeof(arrInt)/sizeof(arrInt[0]);
for (int i = 0; i (arrInt[i],arrInt[arrIntSize-1-i]));
}
map::iterator objMapI=objmap.find(3);
//find()返回迭代器类型为 map::iterator
//不能使用pair objPair来获得返回值
objmap[12];
cout<<"The value of key 3 is : "<<(*objMapI).second<
无序关联容器也是通过键查找值,但其通过哈希表实现,且元素也是无序的,能够快速存取元素。无序关联容器通过哈希函数计算某个对象的索引,索引相同的对象放在一个桶中,进而只在索引对应的桶中搜索。理想情况下,应有足够多的桶,每个桶只包含为数不多的对象。主要有unordered_map、unordered_set、unordered_multimap三种。
函数 | 说明 |
X::key_type | 表示键Key,类型 |
X::key_equal | 二元谓词binary predicate,用于表示两个类型的Key参数是否相同 |
X::hasher | Hash函数,用于计算索引值的二元函数对象 |
X::local_iterator | 实际上是一个类型与 X::iterator 相同的迭代器,但只能用于一个桶(即一个索引) |
X::const_local_iterator | 实际上是一个类型与 X::const_iterator 相同的迭代器,但只能用于一个桶(即一个索引) |
X::mapped_type | unordered_map 、unordered_multimap中关联数据类型 |
函数 | 说明 |
X Alloc=allocator |
创建键类型为KT、值类型为VT的无序关联容器对象A,比较对象c默认为less |
X(n, hf, eq) | 创建一个至少包含 n 个桶的匿名空容器,该无序关联容器将 hf 用作哈希函数,将eq用作键值相等谓词。hf默认值为 hasher(), eq默认值为key_equal()。 |
X A(n, hf, eq) | 创建一个至少包含 n 个桶的空容器A,该无序关联容器将 hf 用作哈希函数,将eq用作键值相等谓词。hf默认值为 hasher(), eq默认值为key_equal()。 |
X A(p, q, n, hf, eq) | 创建一个至少包含 n 个桶的空容器,将 hf 用作哈希函数,将 eq 用作键值相等谓词,并插入 区间[p, q]中的元素。n省略时桶数不定;hf默认值为 hasher(), eq默认值为key_equal()。 |
X |
创建键值类型为T的set对象A,其内容为对象B的区间[begin, end)的内容,并使用函数对象c作为比较对象,c默认为less |
A.find(k) | 返回一个迭代器,该迭代器指向键与 k 相同的元素;如果没有找到这样的元素,则返回 a.end() |
A.insert(pair |
插入一个键值对为(v,k)的pair对象 |
A.insert(p, q) | 插入B集合中区间[p,q)的值 |
A.hash_function() | 返回A使用的哈希函数 |
A.key_equal() | 返回A使用的键值相等谓词 |
A.bucket(k) | 返回键值为 k 的元素所属桶的索引 |
A.bucket_size(index) | 返回索引为index的桶的所包含的元素 |
A.bucket_count() | 返回A包含的桶数 |
A.begin(index) | 返回一个迭代器,它指向索引为index的桶中的第一个元素 |
A.end(index) | 返回一个迭代器,它指向索引为index的桶中的最后一个元素 |
A.cbegin(index) | 返回一个常量迭代器(即const_iterator),它指向索引为index的桶中的第一个元素 |
A.cend(index) | 返回一个迭代器,它指向索引为index的桶中的最后一个元素 |
A.rehash(n) | 将桶数调整为不小于 n,并确保A.bucket_count() > A.size() / A.max_load_factor() |
STL中除了容器类的成员方法外,其还含有一些不需要使用容器类对象调用的函数,这些函数主要分为四类:
①非修改式序列操作:面向序列容器的不会修改数据的函数;
②修改式序列操作:面向序列容器的修改数据的函数;
③排序和相关操作
④通用数字运算
前三类放在头文件algorithm,第四类算法放在头文件numeric中。
一般而言,选择容器类的成员方法更加好,因为它更适用于特定容器。
(该部分等使用到了再来补!)