标准模板库(Standard Template Library,STL)是一个基于模板的容器类库。可用STL创建一个类,为任意数据类型定义矢量、链表、队列和栈等操作。STL中的泛型算法(generic algorithm)和函数对象(function object)使算法摆脱了对不同数据类型个性操作的依赖。
STL主要提供三类工具:容器、迭代器和算法。
目录
(一)容器类
(1)顺序容器
(2)关联容器
(3)容器适配器
(二)迭代器
(三)泛型算法
count()算法
count_if()算法
函数对象
容器类是管理序列的类,是容纳一组对象或对象集的类。通过由容器类提供的成员函数,可以实现诸如向序列中插入元素、删除元素、查找元素等操作,这些成员函数通过返回迭代器来指定元素在序列中的位置。容器中提供了很多接口,许多基本操作是所有容器都适用的,而这些操作则适用于类似容器的子集,可以用有类似操作的新类来扩展STL。
STL容器分为三大类:顺序容器、关联容器及容器适配器。
标准库容器类 | 说明 | |
---|---|---|
顺序容器 | voctor(矢量) | 从后面快速插入与删除,直接访问任何元素 |
list(双向链表) | 从任何地方快速插入与删除 | |
deque(双端队列) | 从前面或后面快速插入与删除,直接访问任何元素 | |
关联容器 | set(集合) | 快速查找,不允许重复值 |
multiset(多重集合) | 快速查找,允许重复值 | |
map(映射) | 一对一映射,基于关键字快速查找,不允许重复值 | |
multimap(多重映射) | 一对多映射,基于关键字快速查找,允许重复值 | |
容器适配器 | stack(栈) | 后进先出(LIFO) |
queue(队列) | 先进先出(FIFO) | |
priority_queue(优先级队列) | 最高优先级元素总是第一个出列 |
vector类和deque类以数组为基础,list类以双向链表为基础。
vector类的特点:
容器声明形式如下:
vector vi; //定义存放整形序列的矢量容器,长度为0的空 vector
vector vf(10); //定义存放实形序列的矢量容器,10个初始化为0的元素 vector vd(10,3.14);//定义存放实形序列的矢量容器,10个初始化3.14元素
vector vstr; //定义存放字符串序列的矢量容器
vector vBox(20); //定义存放20个盒子对象的矢量容器
//实例化特定矢量容器时自动调用如下构造函数:
vector(size_t n); //构造一个有n个元素的矢量,每个元素都是由默认的构造函数所建立
vector(size_t n,T& V); //T表示矢量的基本类型,为每个元素用同一个对象V来赋初值。类型T中至少要包括默认构造函数和复制构造函数才能被vector容器接收,有时需要重载复制运算符和比较运算符
vector(first,last); //元素的初值由区间[first,last)指定的序列中的元素复制而来
vector(vector& X); //复制构造函数
测试STL中的vector的功能:
// STLVectorTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
using namespace std;
typedef vector INTVECTOR; //typedef定义一种类型的别名
int main()
{
INTVECTOR vec1; //vec1对象初始为空
INTVECTOR vec2(10,6); //vec2对象最初有10个值为6的元素
INTVECTOR vec3(vec2.begin(),vec2.begin() + 3);//vec3对象最初有3个值为6的元素
INTVECTOR::iterator i; //声明一个名为 i 的双向迭代器
//从前向后显示vec1中的数据
cout<<"vec1.begin()--vec1.end():"<
list类的特点:
deque类的特点:
deque与vector的区别:
关联容器中每个元素在容器的位置与其他元素相关,容器总是按元素的键(key)的大小自动排序。
set容器的特点:
// STLSetTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
using namespace std;
//创建set模板的实例
typedef set SET_INT;
//put_HTest函数,从头向尾显示set容器的所有元素
void put_HTset(SET_INT set1, char *name)
{
SET_INT::iterator it;
cout<s2="<<(s1>s2)<s2=1
请按任意键继续. . .
multiset容器的特点:
map容器的特点:
每个元素都使由类型为Key的键和类型为Data的数据相关联构成的数据对。
map 保存元素的键是唯一的。
元素按键的大小关系自动排序,与插入先后顺序无关。
插入新元素不会使已有的迭代器失效,删除元素也不会使迭代器无效,除非该迭代器指向了被删除的对象。
// STLMapTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
容器适配器包括栈、队列和优先级队。栈(stack)是为堆栈而设计的专用容器,具有后进先出的特性,主要包括压栈和弹出。
适配器的特点:
适配器不独立,它依附在一个顺序容器上。例如声明一个用矢量容器实现的字符型堆栈: stack
适配器没有自己的构造和析构函数。其中,队列(queue)默认用deque问基础,栈(stack)可用vector或deque为基础。
stack类模板声明在
// STLStackTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
using namespace std;
typedef stack STACK_INT;
int _tmain(int argc, _TCHAR* argv[])
{
STACK_INT stack1;
int i;
//判断栈是否为空
cout<<"stack1.empty() returned "<<(stack1.empty()?"true":"false")<
迭代器是指针概念的泛型化,它指向容器中的元素。迭代器可以包括指针,但它又不仅仅是一个指针。迭代器把算法与容器连接起来,算法本身与容器无关,只是间接通过迭代化器操作容器元素。迭代器主要有四种类型:输入输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。
STL中普通类型迭代器按照基本访问功能主要分为五种,根据功能灵活性分为四级(输入输出为一级),功能最强且最灵活的是随机访问迭代器。
标准库迭代子类型 | 说明 |
---|---|
输入 InputIterator |
从容器中读取元素,输入迭代子只能一次一个元素地向前移动(即从容器开头到容器末尾)。要重读必须从头开始 |
输出 OutputIterator |
向容器写入元素,输出迭代子只能一次一个元素地向前移动。输出迭代子要重写,必须从头开始 |
正向 ForwardIterator |
组合输入迭代子和输出迭代子的功能,并保留在容器中的位置(作为状态信息),所以重新读写不必从头开始 |
双向 BidirectionalIterator |
组合正向迭代子功能与逆向移动功能(即从容器序列末尾到容器序列开头) |
随机访问 RandomAccessIterator |
组合双向迭代子的功能,并能直接访问容器中的任意元素,即可向前或向后调任意个元素 |
迭代操作 | 说明 |
---|---|
所有迭代子 ++p p++ |
前置自增迭代子,先++后使用 后置自增迭代子,先使用后++ |
输入迭代子 *p p = p1 p == p1 p != p1 |
间接引用迭代子,作为右值 将一个迭代子赋给另一个迭代子 比较迭代子的相等性 比较迭代子的不等性 |
输出迭代子 *p p = p1 |
间接引用迭代子,作为左值 将一个迭代子赋给另一个迭代子 |
正向迭代子 | 提供输入和输出迭代子的所有功能 |
双向迭代子 --p p-- |
包含正向迭代子所有功能,再增加 先--再使用,前置自减迭代子 先使用后--,后置自减迭代子 |
随机访问迭代子 p += i p -= i p + i p - i p[i] p < p1 p <= p1 p >= p1 p > p1 |
包含双向迭代子所有功能,再增加 迭代子p递增i位(后移i位)(p本身变) 迭代子p递减i位(前移i位)(p本身变) 在p所在位置后移i位后的迭代子(迭代子p本身不变) 在p所在位置前移i位后的迭代子(迭代子p本身不变) 返回与p所在位置后移i位的元素引用 如迭代子p小于p1,返回true,所谓小,即p在p1之前 如迭代子p小于等于p1,返回true,否则返回false 如迭代子p大于等于p1,返回true,所谓大,即p在p1之后 如迭代子p大于p1,返回true,否则返回false |
STL中迭代器的使用:
// STLIteratorTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
using namespace std;
//创建一个list容器的实例LISTINT,其存放Int型数据
typedef list LISTINT;
int _tmain(int argc, _TCHAR* argv[])
{
//用LISTINT创建一个名为listOne的list对象
LISTINT listOne;
//指定 i 为迭代器变量
LISTINT::iterator i; //迭代器
LISTINT::reverse_iterator ir; //反向迭代器
//从前面向listOne容器中添加数据
listOne.push_front(2);
listOne.push_front(1);
//从后面向listOne容器中添加数据
listOne.push_back(3);
listOne.push_back(4);
//从前向后显示listOne中的数据
for(i = listOne.begin();i != listOne.end(); ++ i)
cout<< *i <<" ";
cout<>(*i);
}
//从前向后显示listOne中的数据
for(i = listOne.begin();i != listOne.end(); ++ i)
cout<< *i <<" ";
cout<
算法为迭代器访问的对象组提供了计算和分析的函数。在模板中算法不依赖于具体的数据类型,而泛型算法更进一步不依赖于具体的容器,它们应用于可以通过迭代器访问的任何序列。泛型算法中采用函数对象引入不同情况下同一算法的差异。它没有使用继承和多态,避免了虚函数的开销,是STL 效率更高。
算法是STL中最大的工具集合。主要分三类:
不修改序列的操作 | find()、find_end()、find_first()、adjacent_find()、cout()、equal()、mismatch()、search()等 |
修改序列的操作 | swap()、copy()、transform()、replace()、remove()、reverse()、rotate()、fill()等 |
排序、合并和相关操作 | sort()、stable_sort()、binary_search()、merge()、min()、max()等 |
典型算法:
所有泛型算法的前两个实参是一对 iterator ,通常称为 first 和 last,它们标出要操作的容器或内置数组中的元素范围。元素的范围,包括 first,但不包含last的左闭合区间,即 [first, last)。当 first == last 成立,则范围为空。对iterator的属性,则要求在每个算法声明中指出,所声明的是最低要求。如 find() 算法的最低要求为:InputIterator;还可以传递更高级别的迭代子。如:ForwardIterator、BidirectionalIterator、RandomAccessIterator,但不能用 OutputIterator。
count()算法是独立于容器类模板的非成员函数,对于对STL的各种容器包括数组进行元素个数统计。定义在
template
size_t count(Init first, Init last, const T&val);
算法的使用:
// countSTLTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
#define size 10
//产生指定范围的整数随机数
int getrand(int min,int max)
{
int m;
m = max - min;
m = min + double(rand()) / RAND_MAX * m;
return m;
}
//利用类模板生成实例
typedef vector IntArray;
typedef list LISTINT;
typedef set SET_INT;
int main()
{
//------------------------------
//count算法对于普通数组的计算
//------------------------------
int x[size];
cout<<"x[]:";
for(int i = 0; i < size; i ++)
{
x[i] = getrand(1,3);
cout<
count_if()算法是独立于容器类模板的非成员函数,对于对STL的各种容器包括数组进行元素个数统计。定义在
template
size_t count_if(Init first, Init last, Pred pr);
//对容器中的 [first,last) 区间的每个元素执行一次 Pred pr函数,返回符合条件的元素个数
//pr为要执行的函数,此函数由用户根据需要自己定义
算法的使用:
// count_ifSTLTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
//如果字符串以'S'开头,则返回true
int MatchFirstChar(const string& str)
{
string s("S");
return s == str.substr(0,1);
}
int main()
{
const int VECTOR_SIZE = 8;
//生成成员类型为string的vector容器类
typedef vector StringVector;
//定义迭代器类型
typedef StringVector::iterator StringVectorIt;
//声明vector容器的对象
StringVector NamesVect(VECTOR_SIZE);
//声明迭代器
StringVectorIt start,end,it;
int result = 0; //存放统计数据
//初始化vector容器NamesVect
NamesVect[0] = "She";
NamesVect[1] = "Sells";
NamesVect[2] = "Sea";
NamesVect[3] = "Shells";
NamesVect[4] = "by";
NamesVect[5] = "the";
NamesVect[6] = "Sea";
NamesVect[7] = "Shore";
//设置容器的起始位置和终止位置
start = NamesVect.begin();
end = NamesVect.end();
//显示NamesVect容器的元素
cout<<"NamesVect:";
for(it = start;it != end;it ++)
cout<<*it<<" ";
cout<
每个泛型算法的实现都独立于容器类型,它消除了算法对类型的依赖性。当一个算法应用于一个具体的容器时,STL的泛型算法采用“函数对象”(Function Object)来解决该问题。
函数对象是一个类对象,通常它仅有一个成员函数,该函数重载了函数调用操作符(operator())。函数对象亦称为拟函数对象(Function_Like Object)和函子(Functor)。该操作符封装了应该被实现为一个函数的操作。具体情况下,函数对象被作为实参传递给泛型算法。和引用一样,函数对象很少独立使用。
使用函数对象实现泛型算法,可将其作为泛型算法的实参使用,通常用来改变默认的操作,如:
sort(vec.begin(), vec.end(), greater());
这里把整数的大于关系函数对象作为实参,得降序排列。如果是字符串,则只需要改一下参数就可以用于字符串的排序:
sort(svec.begin(), svec.end(), greater());
例如:以函数对象作为“求序列中最小值的函数模板”中的“数值比较算法”中的参数:
//Comp为比较函数对象类模板,对不同的数据类型,可以产生不同的比较函数,以实现泛型类
template
const Type& min(const Type* p,int size,Comp comp)
{
int minIndex = 0;
//最小元素下标初值为0,即设0号元素最小
for(int i = 1; i < size; i ++)
if(comp(p[i],p[minIndex]))
minIndex = i;
return p[minIndex];
}
函数对象一般有以下几种:
函数对象和指针的功能相似,使用函数对象的优点:
函数对象定义和测试如下:
// sumFunctionObjectTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
using namespace std;
template
class Sum
{
T res;
public:
Sum(T i = 0):res(i){} //构造函数,即Sum(T i = 0){res = i;}
T operator()(T x) //累加,重载的调用操作符
{
res += x;
return res;
}
};
template
T Func1(FuncObject fob,const T &val) //函数对象作为参数
{
return fob(val); //调用重载的(),实现加法
}
template
T Func2(FuncObject &fob,const T &val) //参数为引用,建议用此方式
{
return fob(val); //调用重载的(),实现加法
}
int main()
{
Sum sum(10); //调用构造函数建立sum,res值为10
int i = 5, j = 10;
//cout<(5),i)<<'\t'; //5+i,输出10
cout<(),j)<
问题:
cout< 查阅相关博客,部分作者给出的解释是: 对于 cout<<函数1<<函数2; ,通常 先算函数2,再函数1。