泛型就是通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。
C++引入模板的概念,主要包括函数模板和类模板
1、函数模板
1.1 函数模板定义及其实例化
求两个数中的大者,分别考虑整数、长整数、实数的情况
如果没有采用模板,一般用函数重载,如下:
#include <iostream>
using namespace std;
int Max(int x,int y) //整数比较
{ return x>y? x:y ; } //长整数比较
long Max(long x, long y)
{ return x>y? x:y ; }
double Max(double x, double y) //实数比较
{ return x>y? x:y ; }
int main(){
int a=12,b=34,m;
long c=67890,d=67899,n;
double e=12.34,f=56.78,p;
m=Max(a,b);
n=Max(c,d);
p=Max(e,f) ;
cout<< "int_max="<<m<<endl;
cout<< "long_max="<<n<<endl;
cout<< "double_max="<<p <<endl ;
return 0;
}
程序运行结果如下:
int_max=34
long_max=67899
double_max=56.78
如果采用函数模板,只需写一次即可#include <iostream>
using namespace std;
template <class T>
T Max(T x,T y ) { return x>y? x:y ; }
int main(){
int a=12,b=34,m;
long c=67890,d=67899,n;
double e=12.34,f=56.78,p;
m=Max(a,b);//调用函数模板,此时T被int取代
n=Max(c,d); //调用函数模板,此时T被long取代
p=Max(e,f) ; //调用函数模板,此时T被double取代
cout<< "int_max="<<m<<endl;
cout<< "long_max="<<n <<endl;
cout<< "double_max="<<p <<endl ;
return 0; }
template <typename T>
int a;//错误,不允许在此位置有任何语句
T Max(T x,T y){ … }
即函数模板--->实例化----->模板函数。
例如,在程序中,当编译器遇到
template <typename T>
T Max(T x,T y){ … }
时,并不会产生任何代码,但当它遇到函数调用Max(a,b),编译器会将函数名Max与模板Max相匹配,将实参的类型取代函数模板中的虚拟类型T,生成下面的模板函数:
1.2 模板参数的匹配问题
C++在实例化函数模板的过程中,只是简单地将模板参数替换成调用实参的类型,并以此生成模板函数,不会进行参数类型的任何转换。这种方式与普通函数的参数处理有着极大的区别,在普通函数的调用过程中,C++会对类型不匹配的参数进行隐式的类型转换
#include <iostream>
using namespace std;
template <typename T1,typename T2>
T1 Max(T1 x,T2 y){ return x>y? x:y ; }
int main(){
cout<<"2,2.3两数中的大者为:"<<Max(2,2.3)<<endl;
cout<<" 'a',2 两数中的大者为:"<<Max('a', 2)<<endl;
return 0; }
#include <iostream>
using namespace std;
template <typename T,int size>
void BubbleSort(T a[])
{ int i,j; bool change;
for(i=size-1,change=true;i>=1 && change;--i)
{ change=false;
for( j=0;j<i;++j)
if(a[j]>a[j+1])
{ T temp; temp=a[j];a[j]=a[j+1];a[j+1]=temp;
change=true; }
} }
int main()
{ int a[]={9,7,5,3,1,0,2,4,6,8};
char b[]={'A','C','E','F','D','B','U','V','W','Q'};
int i;
cout<<"********a数组********"<<endl;
cout<<"排序前:"<<endl;
for(i=0;i<10;i++) cout<<a[i]<<" ";
cout<<endl;
BubbleSort<int,10>(a);
cout<<"排序后:"<<endl;
for(i=0;i<10;i++) cout<<a[i]<<" ";
cout<<endl;
cout<<"********b数组********"<<endl;
cout<<"排序前:"<<endl;
for(i=0;i<10;i++) cout<<b[i]<<" ";
cout<<endl;
BubbleSort<char,10>(b);
cout<<"排序后:"<<endl;
for(i=0;i<10;i++) cout<<b[i]<<" ";
cout<<endl;
return 0;
}
程序运行结果如下:
********a数组********
排序前:
9 7 5 3 1 0 2 4 6 8
排序后:
0 1 2 3 4 5 6 7 8 9
********b数组********
排序前:
A C E F D B U V W Q
排序后:
A B C D E F Q U V W
1.4 函数模板重载
char *Max(char *x,char *y)
{ cout<<"This is the overload function with char*,char*!maxis:";
return strcmp(x,y)>0?x:y;
}
优先处理重载函数,如果没有重载函数匹配,然后在处理模板的实例化。
编写求2个数、3个数和一组数中最大数的函数模板。
#include <iostream>
#include <string>
using namespace std;
template <typename T> //声明函数模板
T Max(T x,T y) { return x>y? x:y; }
template <typename T> //函数模板重载
T Max(T x,T y,T z)
{ if(x<y) x=y; if(x<z) x=z; return x; }
template <typename T> //函数模板重载
T Max(T a[],int n) //求数组a[n]中的最大数
{ T temp=a[0];
for(int i=1;i<n;i++)
if (temp<a[i]) temp=a[i];
return temp;
}
int main(){
string s1="Beijing 2008",s2="Welcome to Beijing!";
int a[]={1,2,3,4,5,6,7,8,9};
cout<<Max(2,3)<<endl;
cout<<Max(2.02,3.03,4.04)<<endl;
cout<<Max(s1,s2)<<endl;
cout<<Max(a,9)<<endl;
return 0;
}
2、类模板
template<classT1, classT2 , … >
class类名
{
类体
} ;
class 也可以用 typename 代替//Stack.h
const int SSize=10; //SSize为栈的容量大小
template <class T > //声明类模板,T为类型参数
class Stack
{public:
Stack(){top=0;}
void Push(T e); //入栈操作
T Pop(); //出栈操作
bool StackEmpty(){return top==0;}
bool StackFull(){ return top==SSize;}
private:
T data[SSize]; //栈元素数组,固定大小为SSize
int top; //栈顶指针
};
template <class T > //push成员函数的类外定义
void Stack<T >::Push(T e)
{ if(top==SSize)
{ cout<<"Stack is Full!Don't push data!"<<endl;
return;
}
data[top++]=e;
}
template <class T>
inline T Stack<T >::Pop()
{ if(top==0)
{ cout<<"Stack is Empty!Don't pop data!"<<endl;
return 0;
}
top--;
return data[top];
}
注意:在引用模板的类名的地方 必须伴有该模板的参数名表。如:void Stack<T>::Push(T e){ … }
class Stack{
public:
Stack(){top=0;}
void Push(int e); //入栈操作
int Pop(); //出栈操作
boolStackEmpty(){returntop==0;} //判断栈是否为空
boolStackFull(){return top==10;} //判断栈是否已满
private:
int data[10];
int top; //栈顶指针
};
类模板、模板类及模板对象之间的关系图
类模板名<实际类型名表> 对象名;
类模板名<实际类型名表> 对象名(实参表);
由 类模板 创建其实例模板类时,必须为类模板的每个模板参数显式指定模板实参。然而由 函数模板 创建其实例模板函数时,可以不显式指定模板实参,这时编译器会自动根据函数调用时的实参来推断出Stack<int> int_stack;
for(int i=1;i<10;i++) int_stack.Push(i);
return 0 ;
2.2.1 非类型参数
//Stack.h
template <class T,int SSize>
class Stack
{public:
Stack(){top=0;}
void Push(T e); //入栈操作
T Pop(); //出栈操作
bool StackEmpty(){return top==0;}
bool StackFull(){ return top=SSize;}
private:
T data[SSize]; //栈元素数组,固定大小为SSize
int top; //栈顶指针
};
//Push成员函数的类外定义
template <class T,int SSize>
void Stack<T,SSize>::Push(T e)
{ if(top==SSize)
{ cout<<"Stack is Full!Don't push data!"<<endl;
return;
}
data[top++]=e;
}
// Pop成员函数的类外定义,指定为内联函数
template <class T,int SSize>
inline T Stack<T,SSize>::Pop()
{ if(top==0)
{ cout<<"Stack is Empty!Don't pop data!"<<endl;
return 0;
}
top--;
return data[top];
}
template<class T,int SSize=10>
class Stack
{public:
…
private:
Tdata[SSize]; //栈元素数组,固定大小为SSize
int top; //栈顶指针
};
template<class T=int,int SSize=10>
classStack
{public:
…
private:
T data[SSize];//栈元素数组,固定大小为SSize
int top; //栈顶指针
};
Stack<> mystack; //same as Stack<int,10>
模板类型的模板参数类模板的模板形参表中的参数类型有3种:类型参数、非类型参数、类模板类型的参数,函数模板的模板参数类型也与此相同。下面看一个类模板类型的模板参数的例子。
类模板类型的模板参数举例
#include <iostream>
using namespace std;
template <class T,size_t size>
class Array{
T data[size];
size_t count; //数组元素个数
public:
Array(){count=0;} //构造函数
void PushBack(const T& t)
{ if(count<size) data[count++]=t; }
void PopBack() { if(count>0) - -count; }
T* Begin(){return data;}
T* End(){return data+count;}
};
//声明Container类模板,
//它有一个类模板类型的模板参数Seq
template <class T,size_t size,template
<class,size_t> class Seq>
class Container{
Seq<T,size> seq;
public:
void Append(const T& t){ seq.PushBack(t);}
T* Begin(){return seq.Begin();}
T* End(){return seq.End();}
};
int main()
{ const size_t N=10;
container<int,N,Array> container;
container.Append(1);
container.Append(2);
int *p=container.Begin();
while(p!=container.End())
cout<<*p++<<endl;
return 0;
}