STL应用
1. 前言
stl标准模板库,提供一些常用得数据结构和算法。STL定义了一套概念体系,为范型程序设计提供了逻辑基础。
2. STL基本组件
以下是STL所涉及的四种基本组件。
2.1 容器
容器是容纳、包含一组元素的对象。 分为顺序容器和关联容器两种基本类型。
2.1.1 顺序容器
array(数组)、vector(向量)、deque(双端队列)、forward_list(单链表)、list(列表)
以上这些在逻辑上可以看作长度可扩展的数组
由于元素线性排列,所以可以随时在指定位置插入元素和删除元素
- 但必须符合Assignable这一概念,也就是具有公有的复制构造函数并且可以用“=”赋值
- 以上那些顺序容器中
array
对象的大小固定,forward_list
有特殊的添加和删除操作
2.1.2(有序)关联容器
有序容器中键(key)按顺序存储
- set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)
代码:
void TestMap(){
map<string,int> stu;
stu["01"] = 100;
stu["10"] = 99;
stu["03"] = 98;
stu["10"] = 80;//由于map不具有覆盖功能所以输出时,10号学生的成绩为99
for(map<string,int>::iterator it = stu.begin();it!=stu.end();it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
void TestSet(){
vector<studentInfo> students;
students.push_back(studentInfo("10021","Zheng jiawen"));
students.push_back(studentInfo("10002","Li si"));
students.push_back(studentInfo("10003","Wang wu"));
students.push_back(studentInfo("100011","Li yue"));
students.push_back(studentInfo("10010","Wu si"));
set<studentInfo> studentSet(students.begin(),students.end());
outputCont("student set",cout,studentSet.begin(),studentSet.end());
}
结果:
01 100
03 98
2.1.3 无序关联容器
无序容器按照哈希函数组织元素
- unordered_set(无序集合)、unordered_multiset(无序多重集合)
- unordered_map(无序映射)、unordered_multimap(无序多重映射)
2.1.4 通用功能
序列式容器的排列次序取决于插入时机和位置。
关联式容器的排列次序取决于特定准则。
- 用默认构造函数构造空容器
- 支持关系运算符: ==、!=、<、<=、>、>=
- end():末个元素的后面
- begin():获得容器首迭代器
- cbegin()、cend(): 获取容器首、尾常迭代器,不需要改变容器时更加安全
- clear(): 将容器清空
- empty():判断容器是否为空
- size():得到容器元素个数
- s1. swap(s2):将s1和s2两容器内容交换
- S:: iterator:指向容器元素的迭代器类型(s为容器类型)
- S: :const_ iterator: 常迭代器类型(同上)
代码:
outputCont("vb",cout,vb.begin(),vb.end());
outputCont("vc",cout,vc.begin(),vc.end());
vb.swap(vc);
outputCont("vb swaped",cout,vb.begin(),vb.end());
结果:
vb:0 1 1 1 1
vc:0 0 0 0 0
vb swaped:0 0 0 0 0
2.1.5 对可逆容器的访问
STL为每个可逆容器都提供了逆向迭代器,逆向迭代器可以通过下面的成员函数得到:
- rbegin() :指向容器尾的逆向迭代器
- rend(): 指向容器首的逆向迭代器
逆向迭代器的类型名的表示方式如下(S表示容器类型) :
- S::reverse_ iterator: 逆向迭代器类型
- S: :const_ reverse_ iterator:逆向常迭代器类型
2.1.6 随机访问容器
随机访问容器支持对容器的元素进行随机访问
- s[n]: 获得容器s的第n个元素.
- vector和deque适用
2.2 迭代器
迭代器式算法个容器的桥梁
- 迭代器用作访问容器中的元素
- 算法不直接操作容器中的数据,而是通过迭代器简介操作
算法和容器是独立的
- 增加新的算法,无需影响容器的实现
- 增加新的容器,原有的算法也能用
2.2.1 迭代器的操作
- 迭代器是泛化的指针,提供了类似指针的操作
- 输入迭代器:可以用来从序列中读取数据
- 输出迭代器:允许向序列中写入数据
- 前向迭代器:既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历
- 双向迭代器:与前向迭代器相似,但可以在两个方向上都可以对数组遍历
- 随机访问迭代器:也是双向迭代器,但能够在序列中的任意两个位置之间进行跳转
2.2.2 迭代器的区间
- 两个迭代器表示一个区间:[p1,p2)
- STL算法以迭代器的区间作为输入,传递输入数据
- 区间包含P1不包含P2
- 合法的区间:p1经过n次(n>0)自增(++)操作后满足p1==p2
2.3 函数对象
函数对象是一个行为类似函数的对象,对它可以像调用函数一样调用。函数对象是泛化函数
2.4 算法
使用STL的算法需要包含头文件<algorithm>
如下所示,是transfrom算法的示例
template <typename inputIter,typename outputIter,typename MyOperator>
void transInvT(inputIter begInput,inputIter endInput,
outputIter begOutPut,MyOperator op){
for(;begInput!=endInput;begInput++,begOutPut++){
//*begOutPut = -(begInput);
*begOutPut = op(*begInput);//op:一个函数,说明了具体实现的操作,实现通用
}
}
transInvT(a,b,nNum);
outputCont("InvT a T",cout,b,b+nNum);
输出
InvT a T:-1 -2 -3 -6 -4
3.映射(map)
映射与集合同属于单重关联容器,主要区别在于集合的元素类型是键本身,而映射的元素类型是由键和附加数据所构成的二元组。
-有key和value,按照value来排序
- 在集合中按照键查找一个元素时,一般只是用来确定这个元素是否存在,而在映射中按照键查找一个元素时,除了能确定它的存在我外,还可以得到相应的附加数据。
代码:
//map不具有覆盖功能
void TestMap(){
map<string,int> stu;
stu["01"] = 100;
stu["10"] = 99;
stu["03"] = 98;
stu["10"] = 80;//由于map不具有覆盖功能所以输出时,10号学生的成绩为99
for(map<string,int>::iterator it = stu.begin();it!=stu.end();it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
结果:
01 100
03 98
10 80
4.集合(set)
集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效查找指定元素,也可以方便地得到指定大小范围地元素在容器中所处的区间。
用平衡二叉树存储。
- key和value一样
代码:
void TestSet(){
vector<studentInfo> students;
//添加相关信息
students.push_back(studentInfo("10021","Zheng jiawen"));
students.push_back(studentInfo("10002","Li si"));
students.push_back(studentInfo("10003","Wang wu"));
students.push_back(studentInfo("100011","Li yue"));
students.push_back(studentInfo("10010","Wu si"));
set<studentInfo> studentSet(students.begin(),students.end());
outputCont("student set",cout,studentSet.begin(),studentSet.end());
}
结果:
student set:100011 Li yue
10002 Li si
10003 Wang wu
10010 Wu si
10021 Zheng jiawen
5. 代码
#include "stl.h"
#include <vector>
#include <map>
#include <iostream>
#include <algorithm>//算法库
#include <functional>
#include <set>
using namespace std;
stl::stl(){
}
//取负
void transInv(int a[],int b[],int nNum){
for(int i=0;i<nNum;i++){
b[i] = -a[i];
}
}
//求数的平方
void transSqr(int a[],int b[],int nNum){
for(int i=0;i<nNum;i++){
b[i] = a[i] * a[i];
}
}
//加入模板的取负
template <typename T>
void transInvT(T a[],T b[],int nNum){
for(int i = 0;i<nNum;i++){
b[i] = -a[i];
}
}
//定义的moperator函数
template <typename T>
T InvT(T a){
return -a;
}
//二值化的阈值
template <typename T>
class MyThreshold{
public:
MyThreshold(int n = 128):_nThreshold(n){};//初始化
int operator()(T val){//用模板来表示传入的值得类型
return val<_nThreshold?0:1;
}
int _nThreshold;
};
//完成对所有的容器进行操作并保存在另一个容器中
//模板,输入输出
template <typename inputIter,typename outputIter,typename MyOperator>
void transInvT(inputIter begInput,inputIter endInput,
outputIter begOutPut,MyOperator op){
for(;begInput!=endInput;begInput++,begOutPut++){
//*begOutPut = -(begInput);
*begOutPut = op(*begInput);//op:一个函数,说明了具体实现的操作,实现通用
}
}
//输出函数的重载
template <typename T>
void outputCont(string strName,ostream& os, T begin, T end){
os<<strName<<":";
//遍历输出
for(;begin!=end;begin++){
os<<*begin<<"\t";
}
os<<endl;
}
//降序排列
template <typename T>
bool mycomp(T a,T b){
return a>b;
}
//比较函数
template <typename T>
class MyCompC{
public:
bool operator()(const T& x,const T& y) const{
return x>y;
}
};
class studentInfo{
public:
studentInfo(string strNo,string strName){
_strNo = strNo;
_strName = strName;
}
string _strNo;
string _strName;
//构造该类的输出函数
friend ostream& operator<<(ostream& os,const studentInfo& info){
os<<info._strNo<<" "<<info._strName;
os<<'\n';
return os;
}
friend bool operator < (const studentInfo& info1,const studentInfo& info2){
return info1._strNo < info2._strNo;
}
};
void TestSet(){
vector<studentInfo> students;
students.push_back(studentInfo("10021","Zheng jiawen"));
students.push_back(studentInfo("10002","Li si"));
students.push_back(studentInfo("10003","Wang wu"));
students.push_back(studentInfo("100011","Li yue"));
students.push_back(studentInfo("10010","Wu si"));
set<studentInfo> studentSet(students.begin(),students.end());
outputCont("student set",cout,studentSet.begin(),studentSet.end());
}
//map不具有覆盖功能
void TestMap(){
map<string,int> stu;
stu["01"] = 100;
stu["10"] = 99;
stu["03"] = 98;
stu["10"] = 80;//由于map不具有覆盖功能所以输出时,10号学生的成绩为99
for(map<string,int>::iterator it = stu.begin();it!=stu.end();it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
void TestVector(){
vector<studentInfo> students;
students.push_back(studentInfo("10021","Zheng jiawen"));
students.push_back(studentInfo("10002","Li si"));
students.push_back(studentInfo("10003","Wang wu"));
students.push_back(studentInfo("100011","Li yue"));
students.push_back(studentInfo("10010","Wu si"));
vector<studentInfo>::iterator it = students.begin();
advance(it,2);//从第二个开始排列
sort(it,students.end());
outputCont("students sorted",cout,students.begin(),students.end());
students[2]._strName = "jwz";
for(vector<studentInfo>::iterator it = students.begin();
it!=students.end();it++)
{
it->_strName = "jwz";
}
outputCont("students modified",cout,students.begin(),students.end());
}
void Test(){
TestMap();
TestSet();
TestVector();
const int nNum = 5;
int a[nNum] = {1,2,3,6,4};
outputCont("Inv a",cout,a,a+nNum);
int b[nNum] = {};
//输出不输出到b,输出到vector中
vector<double>vb(nNum);
vector<double>vc(nNum);
transInv(a, b, nNum);
outputCont("Inv a",cout,b,b+nNum);
transSqr(a,b,nNum);
outputCont("Sqr a",cout,b,b+nNum);
transInvT(a,b,nNum);
outputCont("InvT a T",cout,b,b+nNum);
//InvT<int> 传入一个int类型的函数InvT
transInvT(a,a+nNum,b,InvT<int>);
transInvT(a,a+nNum,vb.begin(),InvT<int>);
outputCont("Inv a by iter",cout,vb.begin(),vb.end());
transInvT(a,a+nNum,vb.begin(),MyThreshold<int>(2));
outputCont("Inv a by treshold",cout,vb.begin(),vb.end());
//sort:stl里自带得排序函数,默认升序排序
sort(a,a+nNum);
outputCont("a sorted",cout,a,a+nNum);
//模板函数
sort(a,a+nNum,mycomp<int>);
outputCont("a sorted",cout,a,a+nNum);
//greater不是一个函数,是一个类,因为要带一个(),模板类
sort(a,a+nNum,greater<int>());
outputCont("a sorted",cout,a,a+nNum);
sort(a,a+nNum,MyCompC<int>());
outputCont("a sorted",cout,a,a+nNum);
outputCont("vb",cout,vb.begin(),vb.end());
outputCont("vc",cout,vc.begin(),vc.end());
vb.swap(vc);
outputCont("vb swaped",cout,vb.begin(),vb.end());
}
结果
01 100
03 98
10 80
student set:100011 Li yue
10002 Li si
10003 Wang wu
10010 Wu si
10021 Zheng jiawen
students sorted:10021 Zheng jiawen
10002 Li si
100011 Li yue
10003 Wang wu
10010 Wu si
students modified:10021 jwz
10002 jwz
100011 jwz
10003 jwz
10010 jwz
Inv a:1 2 3 6 4
Inv a:-1 -2 -3 -6 -4
Sqr a:1 4 9 36 16
InvT a T:-1 -2 -3 -6 -4
Inv a by iter:-1 -2 -3 -6 -4
Inv a by treshold:0 1 1 1 1
a sorted:1 2 3 4 6
a sorted:6 4 3 2 1
a sorted:6 4 3 2 1
a sorted:6 4 3 2 1
vb:0 1 1 1 1
vc:0 0 0 0 0
vb swaped:0 0 0 0 0
6.问题及总结
- 问题
开始时没有给studentInfo类构造一个输出函数,使得在调用到该类时的所有输出都会报错,最后在该类中定义了一个友元的输出函数解决了这个问题 - 总结
泛型程序设计是一种重要的程序设计方式,合理利用泛型程序设计,可以大大提高软件的复用性。C++中总的来说离不开cpvt四个东西。
- c:class,面向对象,封装性,将一定的数据方法封装,统一到一个类里
- p:继承,
- v:vitrual,多态性,精华所在
- t:template,模板