进阶篇_STL详解(函数模板特化,类模板特化,用模板实现自己的通用算法)

来源:互联网 发布:现货外盘软件 编辑:程序博客网 时间:2024/06/05 02:26

STL ~= 容器 + 算法 + 迭代器。容器用于容纳数据,算法用于处理数据,迭代器像胶水一样将容器和算法紧密地结合在一起。算法通过迭代器来定位和操控容器中的元素,事实上,每个容器都有自己的迭代器,只有容器自己才知道如何访问自己的元素,这使得迭代器就像一个指向容器中元素的普通指针一样。


#include <iostream>#include <vector>#include <algorithm>using namespace std;//工资统计,计算收入大于1000的员工人数 int main(){vector<int> vecSalary;int put;do{cin>>put;if(0 == put)break;vecSalary.push_back(put);}while(true);int total = count_if(vecSalary.begin(), vecSalary.end(), bind2nd(greater<int>(), 1000)); //count_if:返回区间中满足指定条件的元素数目。          cout<<total<<endl; return 0;}
容器vector的使用不像数组那样固定分配内存,其可以根据用户的输入动态地增长,保证内存空间的合理利用,又可以让程序具有很大的可扩张性,处理很大范围内的数据。由此可得到STL的主要几个好处:封装很多常用的数据结构(容器),提供很多常用的通用算法(算法),调试程序更加安全和方便,支持跨平台(使用STL的C++代码可以轻松地移植到其他平台上)。模板是实现代码重用的一种重要机制,它可以实现类型的参数化,把类型定义为参数,而STL正是借助模板的威力建构起来的。

函数模板

如果说把函数比作一个箱子,每个箱子都是专用的(实现具体的不同功能),那函数模板就是一个万能箱子,它可以用来装各种东西,处理各种类型的数据。比如当编译器发现一个函数模板的调用后,将根据实参的实际数据类型来确认是否匹配函数模板中对应的形参,然后生成一个重载函数,称该重载函数为模板函数。根据参数类型的不同,一个函数模板可以随时变成各种不同的重载函数,从而实现对各种数据类型的处理。函数模板可以用来创建一个通用功能的函数,以支持各种不同的数据类型,简化重载函数的设计。函数模板的声明与使用如下:

#include <iostream>using namespace std;//模板函数范例 template <typename T>    //type理解为模板函数中的一个参数,代表抽象的数据类型 T max(const T a, const T b){    return a>b ? a : b;}int main(){    int a = 10;    int b = 20;    int c = std::max<int>(a, b);    //注意写法,不加std::,会error     cout<<c<<endl; //output: 20         float aa = 0.3;    float bb = 0.4;    float cc = std::max(aa, bb);    //max<float>的<...>不写编译器会自动分析生成相应的重载函数,但是有时候必须要指明类型参数以满足特殊需求,比如max<string>     cout<<cc<<endl; //output: 0.4         string aaa = "abcd";    string bbb = "cde";    string ccc = std::max(aaa, bbb);     cout<<ccc<<endl; //output: cde,这里输出cde,不是返回最长的而是返回字符较大的,所以如果需要应该对模板函数进行特化,实现特定类型的模板函数         return 0;}

特化模板函数,实现特定类型的string模板函数,希望返回最长的字符串:

#include <iostream>using namespace std;//特化函数模板string范例template <typename T>//普通函数模板 T max(const T a, const T b){return a>b ? a : b;}template <>//特化string的函数模板 string max<string>(const string a, const string b)//注意这里及以上的写法格式 {return a.size()>b.size() ? a : b; }int main(){int a = 5;int b = 4;float c = 5.3;float d = 4.4;cout<<std::max(a, b)<<endl<<std::max(c, d)<<endl;//output: 5  5.3string x = "chen";string y = "aisda";cout<<std::max(x, y)<<endl; //用普通函数模板output: cde,返回字符较大的cout<<std::max<string>(x, y)<<endl; //用特化函数模板output: chen,返回字符串最长的 return 0;}

类模板

#include <iostream>using namespace std;//特化类模板范例//定义一个比较两个数的类模板compare<T> template <typename T>//typename所定义的标识符实际上就是类模板的参数,模板参数可以是一个可以是多个 class compare{public:compare(T a, T b): m_a(a), m_b(b){}public:T min(){return m_a > m_b ? m_b : m_a;}T max(){return m_a > m_b ? m_a : m_b;}private:T m_a;T m_b;};int main(){compare<int> intcompare(2, 3);cout<<intcompare.max()<<" > "<<intcompare.min()<<endl;compare<float> floatcompare(2.2, 4.4);cout<<floatcompare.max() <<" > "<<floatcompare.min()<<endl;return 0;}/*output: 3 >2  4.4 > 2.2*/ 

模板的实例化

模板的实例化分为:隐式实例化和显式实例化。

compare<int> intcompare(2, 3);//隐式实例化
大多数都采用隐式实例化,具体的显式实例化如果需要时自行再查,注意的是显式实例化类时,所有的类成员也必须实例化。

用模板实现自己的通用算法

#include <iostream>using namespace std;/*软件谁集中,通常有一个撤销undo和恢复redo的通用功能*//*动作容器模板类,使用T作为模板参数*/template <class T>class actioncontainer{public:actioncontainer(){m_nredo = 0;m_nundo = 0;}void add(T value);//向容器中添加新动作 T redo();//恢复上一步动作 T undo();//撤销上一步动作 private:int m_nredo;int m_nundo;const static int ACTION_SIZE = 5;T m_redoAction[ACTION_SIZE]; //使用数组记录恢复和撤销的动作,数组元素的类型也是模板参数,它在类模板实例化时才确定 T m_undoAction[ACTION_SIZE];};/*模板类的成员函数实现*///向容器中添加动作,这里的T可以是一个基本数据类型,也可以是一个自定义类型 template <class T>void actioncontainer<T>::add(T value){//判断容器中的动作数目是否超过容器的容量 if(m_nundo >= ACTION_SIZE){m_nundo = ACTION_SIZE - 1;for(int i=0; i<ACTION_SIZE; ++i)m_undoAction[i] = m_undoAction[i+1];//将容器中已有的动作前移一个位置 }//将新动作添加到容器中m_undoAction[m_nundo++] = value; }//撤销上一步动作 template <class T>T actioncontainer<T>::undo(){m_redoAction[m_nredo++] = m_undoAction[--m_nundo];//将撤销的动作复制到恢复数组中 return m_undoAction[m_nundo];//返回撤销的动作 }//恢复上一步动作 template <class T>T actioncontainer<T>::redo(){m_undoAction[m_nundo++] = m_redoAction[--m_nredo];//将撤销的动作复制到恢复数组中 return m_redoAction[m_nredo];//返回撤销的动作 }int main(){actioncontainer<int> intaction;//定义一个int类型的动作容器,这样这个动作容器就可以容纳int类型的数据 intaction.add(1);//向容器中添加新的动作,也就是整数 intaction.add(2);intaction.add(3);intaction.add(4); int nundo = intaction.undo(); //撤销上一步动作,这时,nundo的值为4nundo = intaction.undo();//再次撤销身上一步动作,nundo的值为3int nredo = intaction.redo(); //恢复上一步动作,这时,nredo的值为3nredo = intaction.redo();//再次恢复身上一步动作,nredo的值为4return 0;} 

以上代码定义了一个类模板actioncontainer<T>,这个类可以容纳动作(add)并且能够进行撤销(undo)和恢复(redo)动作。在这个模板类的内部,使用了两个数组来分别记录撤销和恢复的动作。这里的动作实际上是广义上的一种数据类型,它可以是一个基本数据类型比如int float,也可以是自己定义的数据类型,这样就使得这个动作容器具有广泛的通用性,可以处理各种数据类型。在这个动作容器类模板actioncontainer<T>中,还定义了它的成员函数,用于操作这个容器中的数据,其中包括添加新的动作到容器中、撤销或恢复动作等。当然,这里成员函数同样是对抽象的模板参数进行操作,其中并不涉及具体的数据类型,也就是说,这些操作与具体的数据类型无关。



0 0
原创粉丝点击