第10章 泛型程序设计与C++标准模板库_第1页
第10章 泛型程序设计与C++标准模板库_第2页
第10章 泛型程序设计与C++标准模板库_第3页
第10章 泛型程序设计与C++标准模板库_第4页
第10章 泛型程序设计与C++标准模板库_第5页
已阅读5页,还剩70页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十章

泛型程序设计

与C++标准模板库清华大学郑莉目录10.1泛型程序设计及STL的结构10.2迭代器10.3容器的基本功能与分类10.4顺序容器10.5关联容器10.6函数对象10.7算法10.8综合实例——对个人银行账户管理程序的改进10.9深度探索10.10小结210.1.1泛型程序设计的基本概念编写不依赖于具体数据类型的程序将算法从特定的数据结构中抽象出来,成为通用的C++的模板为泛型程序设计奠定了关键的基础几个术语概念(concept):用来界定具备一定功能的数据类型,如“支持‘<’运算符”的数据类型构成Comparable这一概念;模型(model):符合一个概念的数据类型称为该概念的模型,如int型是Comparable概念的模型。为概念赋予一个名称,并使用该名称作为模板参数名例如,我们将用这样的方式表示insertionSort这样一个函数模板的原型:template<classSortable>voidinsertionSort(Sortablea[],intn);事实上,很多STL的实现代码就是使用概念来命名模板参数的。310.1泛型程序设计及STL的结构10.1.2STL简介标准模板库(StandardTemplateLibrary,简称STL)提供了一些非常常用的数据结构和算法STL程序实例(例10-1):410.1泛型程序设计及STL的结构//包含的头文件略去……usingnamespacestd;intmain(){ constintN=5; vector<int>s(N);

for(inti=0;i<N;i++) cin>>s[i];

transform(s.begin(),s.end(),

ostream_iterator<int>(cout,""),negate<int>()); cout<<endl; return0;}容器迭代器函数对象算法10.1.2STL简介transform算法的一种实现:template<classInputIterator,classOutputIterator,classUnaryFunction>OutputIteratortransform(InputIteratorfirst,InputIteratorlast,OutputIteratorresult,UnaryFunctionop){ for(;first!=last;++first,++result) *result=op(*first); returnresult;}5STL的组成部分6STL是泛型程序设计的一个范例

容器(container)迭代器(iterator)算法(algorithms)函数对象(functionobject)容器(container)算法(algorithm)容器(container)迭代器(iterator)函数对象(function

object)迭代器(iterator)10.1泛型程序设计及STL的结构——10.1.2STL简介

10.2.1输入流迭代器和输出流迭代器输入流迭代器istream_iterator<T>以输入流(如cin)为参数构造可用*(p++)获得下一个输入的元素输出流迭代器ostream_iterator<T>构造时需要提供输出流(如cout)可用(*p++)=x将x输出到输出流二者都属于适配器适配器是用来为已有对象提供新的接口的对象输入流适配器和输出流适配器为流对象提供了迭代器的接口710.2迭代器例10-2从标准输入读入几个实数,分别将它们的平方输出//10_2.cpp#include<iterator>#include<iostream>#include<algorithm>usingnamespacestd;

//求平方的函数doublesquare(doublex){ returnx*x;}

intmain(){ //从标准输入读入若干个实数,分别将它们的平方输出

transform(istream_iterator<double>(cin), istream_iterator<double>(), ostream_iterator<double>(cout,"\t"),square);cout<<endl;return0;}810.2迭代器——10.2.1输入流迭代器和输出流迭代器10.2.2迭代器的分类910.2迭代器输入迭代器

(InputIterator)输出迭代器

(OutputIterator)前向迭代器(ForwardIterator)双向迭代器(BidirectionalIterator)随机访问迭代器(RandomAccessIterator)迭代器支持的操作迭代器是泛化的指针,提供了类似指针的操作(诸如++、*、->运算符)输入迭代器可以用来从序列中读取数据,如输入流迭代器输出迭代器允许向序列中写入数据,如输出流迭代器前向迭代器既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历双向迭代器与前向迭代器相似,但是在两个方向上都可以对数据遍历随机访问迭代器也是双向迭代器,但能够在序列中的任意两个位置之间进行跳转,如指针、使用vector的begin()、end()函数得到的迭代器1010.2迭代器——10.2.2迭代器的分类10.2.3迭代器的区间两个迭代器表示一个区间:[p1,p2)STL算法常以迭代器的区间作为输入,传递输入数据合法的区间p1经过n次(n>0)自增(++)操作后满足p1==p2区间包含p1,但不包含p21110.2迭代器例10-3综合运用几种迭代器的示例//10_3.cpp#include<algorithm>#include<iterator>#include<vector>#include<iostream>usingnamespacestd;

//将来自输入迭代器p的n个T类型的数值排序,将结果通过输出迭代器result输出template<classT,classInputIterator,classOutputIterator>voidmySort(InputIteratorfirst,InputIteratorlast,OutputIteratorresult){ //通过输入迭代器p将输入数据存入向量容器s中

vector<T>s; for(;first!=last;++first) s.push_back(*first); sort(s.begin(),s.end());//对s进行排序,sort函数的参数必须是随机访问迭代器

copy(s.begin(),s.end(),result); //将s序列通过输出迭代器输出}1210.2迭代器——10.2.3迭代器的区间例10-3(续)intmain(){ //将s数组的内容排序后输出

doublea[5]={1.2,2.4,0.8,3.3,3.2}; mySort<double>(a,a+5,ostream_iterator<double>(cout,"")); cout<<endl; //从标准输入读入若干个整数,将排序后的结果输出

mySort<int>(istream_iterator<int>(cin),istream_iterator<int>(),ostream_iterator<int>(cout,"")); cout<<endl; return0;}1310.2迭代器——10.2.3迭代器的区间运行结果:0.81.22.43.23.32-458-136-5-5-4-12356810.2.4迭代器的辅助函数advance(p,n)对p执行n次自增操作distance(first,last)计算两个迭代器first和last的距离,即对first执行多少次“++”操作后能够使得first==last1410.2迭代器10.3容器的基本功能与分类容器类是容纳、包含一组元素或元素集合的对象。七种基本容器:向量(vector)双端队列(deque)列表(list)集合(set)多重集合(multiset)映射(map)多重映射(multimap)1510.3容器的基本功能与分类(续)16容器(Container)随机访问容器

(RandomAccessContainer)可逆容器(ReversibleContainer)容器(Container)顺序容器(Sequence)关联容器

(AssociativeContainer)vectordequelistmultisetmultimapsetmap容器的通用功能容器的通用功能用默认构造函数构造空容器支持关系运算符:==、!=、<、<=、>、>=begin()、end():获得容器首、尾迭代器clear():将容器清空empty():判断容器是否为空size():得到容器元素个数s1.swap(s2):将s1和s2两容器内容交换相关数据类型(S表示容器类型)S::iterator:指向容器元素的迭代器类型S::const_iterator:常迭代器类型1710.3容器的基本功能与分类可逆容器、随机访问容器可逆容器S::reverse_iterator:逆向迭代器类型S::const_reverse_iterator:逆向常迭代器类型rbegin():指向容器尾的逆向迭代器rend():指向容器首的逆向迭代器随机访问容器s[n]:获得容器s的第n个元素1810.3容器的基本功能与分类10.4.1顺序容器的基本功能顺序容器的接口赋值assign插入函数insert,push_front(只对list和deque),push_back删除函数erase,clear,pop_front(只对list和deque)

,pop_back其他顺序容器访问函数front,back改变大小resize1910.4顺序容器例10-4顺序容器的基本操作//10_4.cpp,包含的头文件略去

//输出指定的整型顺序容器的元素template<classT>voidprintContainer(constchar*msg,constT&s){ cout<<msg<<":"; copy(s.begin(),s.end(),ostream_iterator<int>(cout,"")); cout<<endl;}

intmain(){ //从标准输入读入10个整数,将它们分别从s的头部加入

deque<int>s; for(inti=0;i<10;i++){ intx; cin>>x; s.push_front(x); }2010.4顺序容器——10.4.1顺序容器的基本功能例10-4(续) printContainer("dequeatfirst",s); //用s容器的内容的逆序构造列表容器l list<int>l(s.rbegin(),s.rend()); printContainer("listatfirst",l); //将列表容器l的每相邻两个容器顺序颠倒

list<int>::iteratoriter=l.begin(); while(iter!=l.end()){ intv=*iter; iter=l.erase(iter); l.insert(++iter,v); } printContainer("listatlast",l); //用列表容器l的内容给s赋值,将s输出

s.assign(l.begin(),l.end()); printContainer("dequeatlast",s); return0;}2110.4顺序容器——10.4.1顺序容器的基本功能例10-4(续)运行结果如下:0986432154dequeatfirst:4512346890listatfirst:0986432154listatlast:9068341245dequeatlast:90683412452210.4顺序容器——10.4.1顺序容器的基本功能10.4.2三种顺序容器的特性

——向量(Vector)特点一个可以扩展的动态数组随机访问、在尾部插入或删除元素快在中间或头部插入或删除元素慢向量的容量容量(capacity):实际分配空间的大小s.capacity():返回当前容量s.reserve(n):若容量小于n,则对s进行扩展,使其容量至少为n2310.4顺序容器双端队列(deque)特点在两端插入或删除元素快在中间插入或删除元素慢随机访问较快,但比向量容器慢2410.4顺序容器——10.4.2三种顺序容器的特性例10-5奇偶排序先按照从大到小顺序输出奇数,再按照从小到大顺序输出偶数。2510.4顺序容器——10.4.2三种顺序容器的特性//头部分省略intmain(){istream_iterator<int>i1(cin),i2; //建立一对儿输入流迭代器vector<int>s1(i1,i2); //通过输入流迭代器从标准输入流中输入数据sort(s1.begin(),s1.end()); //将输入的整数排序deque<int>s2;//以下循环遍历s1for(vector<int>::iteratoriter=s1.begin();iter!=s1.end();++iter){ if(*iter%2==0) //偶数放到s2尾部

s2.push_back(*iter); else //奇数放到s2首部

s2.push_front(*iter);}//将s2的结果输出copy(s2.begin(),s2.end(),ostream_iterator<int>(cout,""));cout<<endl;return0;}列表(list)特点在任意位置插入和删除元素都很快不支持随机访问接合(splice)操作s1.splice(p,s2,q1,q2):将s2中[q1,q2)移动到s1中p所指向元素之前2610.4顺序容器——10.4.2三种顺序容器的特性例10-6列表容器的splice操作//头部分省略intmain(){ stringnames1[]={"Alice","Helen","Lucy","Susan"}; stringnames2[]={"Bob","David","Levin","Mike"}; list<string>s1(names1,names1+4);//用names1数组的内容构造列表s1 list<string>s2(names2,names2+4);//用names2数组的内容构造列表s2

//将s1的第一个元素放到s2的最后

s2.splice(s2.end(),s1,s1.begin()); list<string>::iteratoriter1=s1.begin();//iter1指向s1首

advance(iter1,2);//iter1前进2个元素,它将指向s1第3个元素

list<string>::iteratoriter2=s2.begin();//iter2指向s2首

++iter2;//iter2前进1个元素,它将指向s2第2个元素

2710.4顺序容器——10.4.2三种顺序容器的特性例10-6(续)28list<string>::iteratoriter3=iter2;//用iter2初始化iter3advance(iter3,2);//iter3前进2个元素,它将指向s2第4个元素//将[iter2,iter3)范围内的结点接到s1中iter1指向的结点前s1.splice(iter1,s2,iter2,iter3);

//分别将s1和s2输出copy(s1.begin(),s1.end(),ostream_iterator<string>(cout,""));cout<<endl;copy(s2.begin(),s2.end(),ostream_iterator<string>(cout,""));cout<<endl;return0;}10.4顺序容器——10.4.2三种顺序容器的特性运行结果:HelenLucyDavidLevinSusanBobMikeAlice三种顺序容器的比较STL所提供的三种顺序容器各有所长也各有所短,我们在编写程序时应当根据我们对容器所需要执行的操作来决定选择哪一种容器。如果需要执行大量的随机访问操作,而且当扩展容器时只需要向容器尾部加入新的元素,就应当选择向量容器vector;如果需要少量的随机访问操作,需要在容器两端插入或删除元素,则应当选择双端队列容器deque;、如果不需要对容器进行随机访问,但是需要在中间位置插入或者删除元素,就应当选择列表容器list。2910.4顺序容器——10.4.2三种顺序容器的特性10.4.3顺序容器的插入迭代器插入迭代器用于向容器头部、尾部或中间指定位置插入元素的迭代器包括前插迭代器(front_inserter)、后插迭代器(back_insrter)和任意位置插入迭代器(inserter)例:list<int>s;back_inserteriter(s);*(iter++)=5;//通过iter把5插入s末尾3010.4顺序容器10.4.4顺序容器的适配器以顺序容器为基础构建一些常用数据结构栈(stack):最先压入的元素最后被弹出队列(queue):最先压入的元素最先被弹出优先级队列(priority_queue):最“大”的元素最先被弹出3110.4顺序容器例10-7利用栈反向输出单词//10_7.cpp,省略头部分intmain(){ stack<char>s; stringstr; cin>>str; //从键盘输入一个字符串

//将字符串的每个元素顺序压入栈中

for(string::iteratoriter=str.begin();iter!=str.end();++iter) s.push(*iter); //将栈中的元素顺序弹出并输出

while(!s.empty()){ cout<<s.top(); s.pop(); } cout<<endl; return0;}3210.4顺序容器——10.4.4顺序容器的适配器运行结果如下:congratulationssnoitalutargnoc优先级队列优先级队列也像栈和队列一样支持元素的压入和弹出,但元素弹出的顺序与元素的大小有关,每次弹出的总是容器中最“大”的一个元素。template<classT,classSequence=vector<T>>classpriority_queue;例10-8细胞分裂模拟一种细胞在诞生(即上次分裂)后会在500到2000秒内分裂为两个细胞,每个细胞又按照同样的规律继续分裂。3310.4顺序容器——10.4.4顺序容器的适配器//10.8.cpp,头部分省略constintSPLIT_TIME_MIN=500;//细胞分裂最短时间constintSPLIT_TIME_MAX=2000;//细胞分裂最长时间

classCell;priority_queue<Cell>cellQueue;

classCell{ //细胞类private: staticintcount; //细胞总数

intid; //当前细胞编号

inttime; //细胞分裂时间public: Cell(intbirth):id(count++){ //birth为细胞诞生时间

//初始化,确定细胞分裂时间

time=birth+(rand()%(SPLIT_TIME_MAX-SPLIT_TIME_MIN))+SPLIT_TIME_MIN; } intgetId()const{returnid;} //得到细胞编号

intgetSplitTime()const{returntime;} //得到细胞分裂时间

booloperator<(constCell&s)const{returntime>s.time;}//定义“<”

34例10-8(续)10.4顺序容器——10.4.4顺序容器的适配器//细胞分裂

voidsplit(){ Cellchild1(time),child2(time); //建立两个子细胞

cout<<time<<"s:Cell#"<<id<<"splitsto#" <<child1.getId()<<"and#"<<child2.getId()<<endl; cellQueue.push(child1); //将第一个子细胞压入优先级队列

cellQueue.push(child2); //将第二个子细胞压入优先级队列

}};intCell::count=0;

intmain(){ srand(static_cast<unsigned>(time(0))); intt; //模拟时间长度

cout<<"Simulationtime:"; cin>>t; cellQueue.push(Cell(0)); //将第一个细胞压入优先级队列

while(cellQueue.top().getSplitTime()<=t){ cellQueue.top().split(); //模拟下一个细胞的分裂

cellQueue.pop(); //将刚刚分裂的细胞弹出

} return0;}35例10-8(续)10.4顺序容器——10.4.4顺序容器的适配器例10-8(续)运行结果如下:Simulationtime:5000971s:Cell#0splitsto#1and#21719s:Cell#1splitsto#3and#41956s:Cell#2splitsto#5and#62845s:Cell#6splitsto#7and#83551s:Cell#3splitsto#9and#103640s:Cell#4splitsto#11and#123919s:Cell#5splitsto#13and#144162s:Cell#10splitsto#15and#164197s:Cell#8splitsto#17and#184317s:Cell#7splitsto#19and#204686s:Cell#13splitsto#21and#224809s:Cell#12splitsto#23and#244818s:Cell#17splitsto#25and#263610.4顺序容器——10.4.4顺序容器的适配器10.5.1关联容器分类和的基本功能关联容器的特点每个关联容器都有一个键(key)可以根据键高效地查找元素接口插入:insert删除:erase查找:find定界:lower_bound、upper_bound、equal_range计数:count3710.5关联容器关联容器概念图3810.5关联容器——10.5.1关联容器的分类和基本功能关联容器(AssociativeContainer)单重关联容器(UniqueAsso-

ciativeContainer)多重关联容器(MultipleAsso-

ciativeContainer)关联容器(AssociativeContainer)简单关联容器(SimpleAsso-

ciativeContainer)二元关联容器(PairAsso-

ciativeContainer)multisetmultimapsetmap四种关联容器单重关联容器(set和map)键值是唯一的,一个键值只能对应一个元素多重关联容器(multiset和multimap)键值是不唯一的,一个键值可以对应多个元素简单关联容器(set和multiset)容器只有一个类型参数,如set<K>、multiset<K>,表示键类型容器的元素就是键本身二元关联容器(map和multimap)容器有两个类型参数,如map<K,V>、multimap<K,V>,分别表示键和附加数据的类型容器的元素类型是pair<K,V>,即由键类型和元素类型复合而成的二元组3910.5关联容器——10.5.1关联容器的分类和基本功能10.5.2集合(set)集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效地查找指定元素,也可以方便地得到指定大小范围的元素在容器中所处的区间。例10-9输入一串实数,将重复的去掉,取最大和最小者的中值,分别输出小于等于此中值和大于等于此中值的实数4010.5关联容器

//10_9.cpp,头部分省略intmain(){ set<double>s; while(true){ doublev; cin>>v; if(v==0)break; //输入0表示结束

pair<set<double>::iterator,bool>r=s.insert(v);//尝试将v插入

if(!r.second) //如果v已存在,输出提示信息

cout<<v<<"isduplicated"<<endl; } set<double>::iteratoriter1=s.begin(); //得到第一个元素的迭代器

set<double>::iteratoriter2=s.end(); //得到末尾的迭代器

doublemedium=(*iter1+*(--iter2))/2; //得到最小和最大元素的中值

//输出小于或等于中值的元素

cout<<"<=medium:" copy(s.begin(),s.upper_bound(medium),ostream_iterator<double>(cout,"")); cout<<endl; //输出大于或等于中值的元素

cout<<">=medium:"; copy(s.lower_bound(medium),s.end(),ostream_iterator<double>(cout,"")); cout<<endl; return0;}4110.5关联容器——10.5.2集合(set)

例10-9(续)例10-9(续)运行结果如下:12.553.55792.505isduplicated2.5isduplicated<=medium:12.53.55>=medium:5794210.5关联容器——10.5.2集合(set)

10.5.3映射(map)映射与集合同属于单重关联容器,它们的主要区别在于,集合的元素类型是键本身,而映射的元素类型是由键和附加数据所构成的二元组。在集合中按照键查找一个元素时,一般只是用来确定这个元素是否存在,而在映射中按照键查找一个元素时,除了能确定它的存在性外,还可以得到相应的附加数据。例10-10有五门课程,每门都有相应学分,从中选择三门,输出学分总和4310.5关联容器

//10_10.cpp,头部分省略intmain(){ map<string,int>courses; //将课程信息插入courses映射中

courses.insert(make_pair("CSAPP",3)); courses.insert(make_pair("C++",2)); courses.insert(make_pair("CSARCH",4)); courses.insert(make_pair("COMPILER",4)); courses.insert(make_pair("OS",5)); intn=3; //剩下的可选次数

intsum=0; //学分总和

while(n>0){ stringname; cin>>name; //输入课程名称

map<string,int>::iteratoriter=courses.find(name);//查找课程

if(iter==courses.end()){ //判断是否找到

cout<<name<<"isnotavailable"<<endl; }else{ sum+=iter->second; //累加学分

courses.erase(iter); //将刚选过的课程从映射中删除

n--; } } cout<<"Totalcredit:"<<sum<<endl; //输出总学分

return0;}4410.5关联容器——10.5.3映射(map)

例10-10(续)例10-10(续)运行结果如下:C++COMPILERC++C++isnotavailableCSAPPTotalcredit:94510.5关联容器——10.5.3映射(map)

例10-11统计一句话中每个字母出现的次数//10_11.cpp,头部分省略intmain(){ map<char,int>s; //用来存储字母出现次数的映射

charc; //存储输入字符

do{ cin>>c; //输入下一个字符

if(isalpha(c)){ //判断是否是字母

c=tolower(c); //将字母转换为小写

s[c]++; //将该字母的出现频率加1 } }while(c!='.'); //碰到“.”则结束输入

//输出每个字母出现次数

for(map<char,int>::iteratoriter=s.begin();iter!=s.end();++iter) cout<<iter->first<<""<<iter->second<<""; cout<<endl; return0;}4610.5关联容器——10.5.3映射(map)

10.5.4多重集合(multiset)与多重映射(multimap)多重集合是允许有重复元素的集合,多重映射是允许一个键对应多个附加数据的映射。多重集合与集合、多重映射与映射的用法差不多,只在几个成员函数上有细微差异,其差异主要表现在去除了键必须唯一的限制。例10-12上课时间查询4710.5关联容器

//10_12.cpp#include<iostream>#include<map>#include<utility>#include<string>usingnamespacestd;intmain(){ multimap<string,string>courses; typedefmultimap<string,string>::iteratorCourseIter;

//将课程上课时间插入courses映射中

courses.insert(make_pair("C++","2-6")); courses.insert(make_pair("COMPILER","3-1")); courses.insert(make_pair("COMPILER","5-2")); courses.insert(make_pair("OS","1-2")); courses.insert(make_pair("OS","4-1")); courses.insert(make_pair("OS","5-5")); //输入一个课程名,直到找到该课程为止,记下每周上课次数

stringname; intcount;

4810.5关联容器——10.5.4多重集合与多重映射例10-12(续) do{ cin>>name; count=courses.count(name); if(count==0) cout<<"Cannotfindthiscourse!"<<endl; }while(count==0); //输出每周上课次数和上课时间

cout<<count<<"lesson(s)perweek:"; pair<CourseIter,CourseIter>range=courses.equal_range(name); for(CourseIteriter=range.first;iter!=range.second;++iter) cout<<iter->second<<""; cout<<endl;

return0;}4910.5关联容器——10.5.4多重集合与多重映射例10-12(续)运行结果如下:JAVACannotfindthiscourse!OS3lesson(s)perweek:1-24-15-510.6.1函数对象函数对象一个行为类似函数的对象可以没有参数,也可以带有若干参数其功能是获取一个值,或者改变操作的状态。例普通函数就是函数对象重载了“()”运算符的类的实例是函数对象5010.6函数对象函数对象概念图5110.6函数对象——10.6.1函数对象函数对象(Function)一元函数对象(UnaryFunction)二元函数对象(BinaryFunction)产生器(Generator)一元谓词(UnaryPredicate)二元谓词(BinaryPredicate)例10-13、例10-14使用两种方式定义表示乘法的函数对象通过定义普通函数(例10-13)通过重载类的“()”运算符(例10-14)用到以下算法:

template<classInputIterator,classType,classBinaryFunction>

Typeaccumulate(InputIteratorfirst,InputIteratorlast,Typeval,BinaryFunctionbinaryOp);对[first,last)区间内的数据进行累“加”,binaryOp为用二元函数对象表示的“加”运算符,val为累“加”的初值5210.6函数对象——10.6.1函数对象#include<iostream>#include<numeric> //包含数值算法头文件usingnamespacestd;//定义一个普通函数intmult(intx,inty){returnx*y;};

intmain(){ inta[]={1,2,3,4,5}; constintN=sizeof(a)/sizeof(int); cout<<"Theresultbymultiplingallelementsinais" <<accumulate(a,a+N,1,mult) <<endl; return0;}5310.6函数对象——10.6.1函数对象例10-13(续)//10_14.cpp#include<iostream>#include<numeric> //包含数值算法头文件usingnamespacestd;

classMultClass {//定义MultClass类public:

intoperator()(intx,inty)const{returnx*y;} //重载操作符operator()};

intmain(){ inta[]={1,2,3,4,5}; constintN=sizeof(a)/sizeof(int); cout<<"Theresultbymultiplingallelementsinais" <<accumulate(a,a+N,1,MultClass()) //将类multclass传递给通用算法

<<endl; return0;}5410.6函数对象——10.6.1函数对象例10-14(续)STL提供的函数对象用于算术运算的函数对象:一元函数对象:negate二元函数对象:plus、minus、multiplies、divides、modulus用于关系运算、逻辑运算的函数对象一元谓词:logical_not二元谓词:equal_to、not_equal_to、greater、less、greater_equal、less_equal、logical_and、logical_or5510.6函数对象——10.6.1函数对象例10-15利用STL标准函数对象//10_15.cpp#include<iostream>#include<numeric>//包含数值算法头文件#include<functional>//包含标准函数对象头文件usingnamespacestd; intmain(){ inta[]={1,2,3,4,5}; constintN=sizeof(a)/sizeof(int); cout<<"TheresultbymultiplingallelementsinAis“<<accumulate(a,a+N,1,multiplies<int>())

<<endl;//将标准函数对象传递给通用算法 return0;}5610.6函数对象——10.6.1函数对象例10-16利用STL中的二元谓词函数对象//10_16.cpp,省略头部分…intmain(){ intintArr[]={30,90,10,40,70,50,20,80}; constintN=sizeof(intArr)/sizeof(int); vector<int>a(intArr,intArr+N);

cout<<"beforesorting:"<<endl; copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t")); cout<<endl;

sort(a.begin(),a.end(),greater<int>());

cout<<"aftersorting:"<<endl; copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t")); cout<<endl; return0;}5710.6函数对象——10.6.1函数对象10.6.2函数适配器绑定适配器将n元函数对象的指定参数绑定为一个常数,得到n-1元函数对象:bind1st、bind2nd组合适配器将指定谓词的结果取反:not1、not2指针函数适配器对一般函数指针使用,使之能够作为其它函数适配器的输入:ptr_fun成员函数适配器对成员函数指针使用,把n元成员函数适配为n+1元函数对象,该函数对象的第一个参数为调用该成员函数时的目的对象:ptr_fun、ptr_fun_ref5810.6函数对象例10-17bind2nd产生binder2nd函数适配器实例//10_17.cpp#include<functional>#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;

intmain(){ intintArr[]={30,90,10,40,70,50,20,80}; constintN=sizeof(intArr)/sizeof(int); vector<int>a(intArr,intArr+N); vector<int>::iteratorp=find_if(a.begin(),a.end(),bind2nd(greater<int>(),40)); if(p==a.end()) cout<<"noelementgreaterthan40"<<endl; else cout<<"firstelementgreaterthan40is:"<<*p<<endl; return0;}5910.6函数对象——10.6.2函数适配器

例10-18ptr_fun、not1和not2产生函数适配器实例//10_18.cpp,头部分省略boolg(intx,inty){ returnx>y;}

intmain(){ intintArr[]={30,90,10,40,70,50,20,80}; constintN=sizeof(intArr)/sizeof(int); vector<int>a(intArr,intArr+N);

vector<int>::iteratorp; p=find_if(a.begin(),a.end(),bind2nd(ptr_fun(g),40)); if(p==a.end()) cout<<"noelementgreaterthan40"<<endl; else cout<<"firstelementgreaterthan40is:"<<*p<<endl;6010.6函数对象——10.6.2函数适配器

6110.6函数对象——10.6.2函数适配器

p=find_if(a.begin(),a.end(),not1(bind2nd(greater<int>(),15))); if(p==a.end()) cout<<"noelementisnotgreaterthan15"<<endl; else cout<<"firstelementthatisnotgreaterthan15is:"<<*p<<endl;

p=find_if(a.begin(),a.end(),bind2nd(not2(greater<int>()),15)); if(p==a.end()) cout<<"noelementisnotgreaterthan15"<<endl; else cout<<"firstelementthatisnotgreaterthan15is:"<<*p<<endl; return0;}例10-17(续)例10-19成员函数适配器实例//10_19.cpp#include<functional>#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;

structCar{ intid; Car(intid){this->id=id;} voiddisplay()const{cout<<"car"<<id<<endl;}};

intmain(){ vector<Car*>pcars; vector<Car>cars;6210.6函数对象——10.6.2函数适配器

6310.6函数对象——10.6.2函数适配器

for(inti=0;i<5;i++) pcars.push_back(newCar(i)); for(inti=5;i<10;i++) cars.push_back(Car(i)); cout<<"elementsinpcars:"<<endl; for_each(pcars.begin(),pcars.end(),std::mem_fun(&Car::display)); cout<<endl;

cout<<"elementsincars:"<<endl; for_each(cars.begin(),cars.end(),std::mem_fun_ref(&Car::display)); cout<<endl;

for(size_ti=0;i<pcars.size();++i) deletepcars[i];

return0;}例10-19(续)10.7.1STL算法基础STL算法本身是一种函数模版通过迭代器获得输入数据通过函数对象对数据进行处理通过迭代器将结果输出STL算法是通用的,独立于具体的数据类型、容器类型STL算法分类不可变序列算法可变序列算法排序和搜索算法数值算法6410.7算法10.7.2不可变序列算法不可变序列算法不直接修改所操作的容器内容的算法用于查找指定元素、比较两个序列是否相等、对元素进行计数等例:template<classInputIterator,classUnaryPredicate>InputIteratorfind_if(InputIteratorfirst,InputIteratorlast,UnaryPredicatepred);用于查找[first,last)区间内pred(x)为真的首个元素6510.7算法例10-20不可变序列算法应用实例//10_20.cpp,头部分省略….

intmain(){ intiarray[]={0,1,2,3,4,5,6,6,6,7,8}; vector<int>ivector(iarray,iarray+sizeof(iarray)/sizeof(int)); intiarray1[]={6,6}; vector<int>ivector1(iarray1,iarray1+sizeof(iarray1)/sizeof(int)); intiarray2[]={5,6}; vector<int>ivector2(iarray2,iarray2+sizeof(iarray2)/sizeof(int)); intiarray3[]={0,1,2,3,4,5,7,7,7,9,7}; vector<int>ivector3(iarray3,iarray3+sizeof(iarray3)/sizeof(int));

//找出ivector之中相邻元素值相等的第一个元素

cout<<*adjacent_find(ivector.begin(),ivector.end())<<endl;

//找出ivector之中小于7的元素个数

cout<<count_if(ivector.begin(),ivector.end(),bind2nd(less<int>(),7))<<endl;6610.7算法——10.7.2不可变序列算法

//找出ivector之中大于2的第一个元素所在位置的元素

cout<<*find_if(ivector.begin(),ivector.end(),bind2nd(greater<int>(),2))<<endl;

//子序列ivector2在ivector中出现的起点位置元素

cout<<*search(ivector.begin(),ivector.end(), ivector2.begin(),ivector2.end())<<endl;

//查找连续出现3个6的起点位置元素

cout<<*search_n(ivector.begin(),ivector.end(),3,6,equal_to<int>())<<endl;

//判断两个区间ivector和ivector3相等否(0为假,1为真) cout<<equal(ivector.begin(),ivector.end(),ivector3.begin())<<endl;

//查找区间ivector3在ivector中不匹配点的位置

pair<vector<int>::iterator,vector<int>::iterator>result=

mismatch(ivector.begin(),ivector.end(),ivector3.begin()); cout<<result.first-ivector.begin()<<endl; return0;}6710.7算法——10.7.2不可变序列算法

例10-20(续)10.7.3可变序列算法可变序列算法可以修改它们所操作的容器对象包括对序列进行复制、删除、替换、倒序、旋转、交换、变换、分割、去重、填充、洗牌的算法及生成一个序列的算法例:template<classForwardIterator,classT>InputIteratorfind_if(ForwardIteratorfirst,ForwardIteratorlast,constT&x);把[first,last)区间内的元素全部改写为x6810.7算法例10-21以可变序列算法对数据序列进行复制,生成,删除,替换,倒序,旋转等可变性操作6910.7算法——10.7.3可变序列算法//10_21.cpp,头部分省略classevenByTwo{private: intx;public: evenByTwo():x(0){} intoperator()(){returnx+=2;}};intmain(){ intiarray1[]={0,1,2,3,4,4,5,5,6,6,6,6,6,7,8}; intiarray2[]={0,1,2,3,4,5,6,6,6,7,8}; vector<int>ivector1(iarray1,iarray1+sizeof(iarray1)/sizeof(int)); vector<int>ivector2(iarray2,iarray2+sizeof(iarray2)/sizeof(int)); vector<int>ivector3(2); ostream_iterator<int>output(cout,"");//定义流迭代器用于输出数据

//迭代遍历ivector3区间,每个元素填上-1

fill(ivector3.begin(),ivector3.end(),-1); copy(ivector3.begin(),ivector3.end(),output);//使用copy进行输出

cout<<endl; //迭代遍历ivector3区间,对每一个元素进行evenByTwo操作

generate(ivector3.begin(),ivector3.end(),evenByTwo()); copy(ivector3.begin(),ivector3.end(),output); cout<<endl;7010.7算法——10.7.3可变序列算法例10-21(续)//将删除元素6后的ivector2序列置于另一个容器ivector4之中

vector<int>ivector4;

remove_copy(ivector2.begin(),ivector2.end(),back_inserter(ivector4),6); copy(ivector4.begin(),ivector4.end(),output); cout<<endl; //删除小于6的元素

ivector2.erase(remove_if(ivector2.begin(),ivector2.end(),bind2nd(less<int>(),6)),ivector2.end()); copy(ivector2.begin(),ivector2.end(),output); cout<<endl; //将所有的元素值6,改为元素值3

replace(ivector2.begin(),ivector2.end(),6,3); copy(ivector2.begin(),ivector2.end(),output); cout<<endl; //逆向重排每一个元素

reverse(ivector2.begin(),ivector2.end()); copy(ivector2.begin(),ivector2.end(),output); cout<<endl; //旋转(互换元素)[first,middle),和[middle,end),结果直接输出

rotate_copy(ivector2.begin(),ivector2.begin()+3,ivector2.end(),output); cout<<endl; return0;}7110.7算法——10.7.3可变序列算法例10-21(续)例10-21(续)运行结果:-1-12401234578666783337887333338737210.7算法——10.7.3可变序列算法10.7.4排序和搜索算法排序和搜索算法对序列进行排序对两有序序列进行合并对有序序列进行搜索有序序列的集合操作堆算法例:template<classRandomAccessIterator,classUnaryPredicate>voidsort(RandomAccessIteratorfirst,RandomAccessIteratorlast,UnaryPredicatecomp);以函数对象comp为“<”,对[first,last)区间内的数据进行排序7310.7算法例10-22排序与搜索算法示例//10_22.cpp,头部分省略…intmain(){ intiarray[]={26,17,15,22,23,33,32,40}; vector<int>ivector(iarray,iarray+sizeof(iarray)/sizeof(int));

//查找并输出第一个最大值元素及其位置

vector<int>::iteratorp=max_element(ivector.begin(),ivector.end()); intn=p-ivector.begin(); cout<<"maxelement:"<<*p<<"foundat"<<n<<endl;

vector<int>ivector1(5);//局部排序并复制到别处

partial_sort_copy(ivector.begin(),ivector.end(),ivector1.begin(),ivector1.end()); copy(ivector1.begin(),ivector1.end(),ostream_iterator<int>(cout,"")); cout<<endl;

sort(ivector.begin(),ivector.end());//排序,缺省为递增。 copy(ivector.begin(),ivector.end(),ostream_iterator<int>(cout,"")); cout<<endl;7410.7算法——10.7.4排序和搜索算法//返回小于等于24和大于等于24的元素的位置

cout<<*lower_bound(ivector.begin(),ivector.end(),24)<<endl; cout<<*upper_bound(ivector.begin(),ivector.end(),24)<<endl;

//对于有序区间,可以用二分查找方法寻找某个元素

cout<<binary_search(ivector.begin(),ivector.end(),33)<<endl;

//合并两个序列ivector和ivector1,并将结果放到ivector2中

vector<int>ivecto

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论