C++模板详解
还记得前面我们学习重载(overloading),为了应付不同情况下调用某一函数的情况,为了提高函数的鲁棒性,我们就可以重载该函数,让他能够自动适应不同的环境。
现在我们换个思路想一下,一般来说,重载函数的功能都是相同的,只不过为了应对不同的输入,而设置了重载函数。但我们自己写的时候很难考虑到所有的情况,有时还是会因重载函数定义不全面而带来调用错误。
那么,我们能不能只写一套代码来解决这个问题呢?
可以的,就是通过模板(template)。
我们通过模板可以进行泛型编程——即以一种独立于任何特定类型的方式编写代码,不指定特定的类,那么理论上任何类型都可以运行。
模板定义
模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。
模版可以分为两类,一个是函数模版,另外一个是类模版。
函数模板
template <typename type>
ret-type func-name(parameter list)
{
// 函数的主体
}
实际上,在函数模板中,模板的用处主要在将参数的类型泛化,它能够自动适应各个类型,在下面的代码中,字符T
就代表所有可能的类型
#include <iostream>
#include <string>
using namespace std;
template <typename T>
T const& Max (T const& a, T const& b)
{
if(a>b)
{
cout<<a<<">"<<b<<endl;
}
else if(a==b)
{
cout<<a<<"等于"<<b<<endl;
}
else
{
cout<<a<<"<"<<b<<endl;
}
return a < b ? b:a;
}
int main ()
{
cout<<"整型比大小,请输入两个整数:"<<endl;
int i,j;
cin>>i;
cin>>j;
cout << "整型比大小结果:" << Max(i, j) << endl;
cout<<"double类型比大小,请输入两个浮点数:"<<endl;;
double f1,f2;
cin>>f1;
cin>>f2;
cout << "double类型比大小" << Max(f1, f2) << endl;
char c1='A';
char c2='K';
cout<<"char类型比大小:"<<Max(c1,c2)<<endl;
// cout<<"整型和浮点数比大小:"<<endl;
// cout<<"请输入一个整数:";
// cin>>i;
// cout<<"请输入一个浮点数"<<endl;
// cin>>f1;
// cout<<"整型和浮点数比大小结果:"<<Max(i,f1);
//错误!调用函数模板时,因为参数都是T类型的,虽然没有指明类型,但必须保证类型一致
return 0;
}
运行结果如下:
整型比大小,请输入两个整数:
56
32
整型比大小结果:56>32
56
double类型比大小,请输入两个浮点数:
65.5
235.5
double类型比大小65.5<235.5
235.5
char类型比大小:A<K
K
在上述程序最后,展示了Max()函数对整型和double类型的比大小,显示错误。这是因为Max()函数输入的形参都是T类型的,这说明两者必须是同一种类型,不能进行不同类型的比大小。
类模板
正如定义函数模板一样,我们也可以定义类模板
一般形式如下:
template <class type>
class class-name
{
}
里面的type是占位符类型名称,可以在类被实例化的时候进行指定。
在生活中,一个人的信息最起码有两个:身份证号,姓名;身份证号可以为int类型,但有时也可以为string类型。
下面的代码就实例化了一个满足身份证号既可以为int型也可以是string型的类:T1代表ID可能的类型,T2代表Name可能的类型。
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Person
{
public:
T1 ID; //身份证号
T2 Name; //其他信息
Person(T1 id,T2 name):ID(id),Name(name){};
void lol()
{
cout<<"id:"<<ID<<endl;
cout<<"name:"<<Name<<endl;
}
};
int main()
{
Person<int,string> ming(117,"Bruce");
ming.lol();
Person<string,string> hei("11x","Jackie");
hei.lol();
return 0;
}
输出如下
id:117
name:Bruce
id:11x
name:Jackie
我们可以看到,实例化一个类模板时,真实类型参数表中的参数是具体的类型名,如 string、int 或其他类的名字,它们用来一一对应地替换类模板定义中“类型参数表”中的类型参数。
在main函数中,其实自动生成了两个不同的类,ming对象的类型为Person<int,string>
,hei对象的类型为Person<string,string>
。
这个编译器由类模板生成类的过程叫类模板的实例化,由类模板实例化得到的类叫模板类。
函数模板作为类模板的成员
类模板的成员函数当然也可以是一个前面提到的函数模板,函数模板只有在被调用时才会被实例化,如下:
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Person
{
public:
T1 ID; //身份证号
T2 Name; //其他信息
Person(T1 id,T2 name):ID(id),Name(name){};
void lol()
{
cout<<"id:"<<ID<<endl;
cout<<"name:"<<Name<<endl;
}
T1 max(T1 a,T1 b)
{
if(a>b)
{
cout<<a<<">"<<b<<endl;
}
else if(a==b)
{
cout<<a<<"等于"<<b<<endl;
}
else
{
cout<<a<<"<"<<b<<endl;
}
return a < b ? b:a;
}
};
int main()
{
Person<int,string> ming(117,"Bruce");
ming.lol();
Person<string,string> hei("11x","Jackie");
hei.lol();
ming.max(123,456);
ming.max(56.2,65.3);
return 0;
}
输出如下
id:117
name:Bruce
id:11x
name:Jackie
123<456
56<65
在修改后的代码中,倒数第二行我们有:ming.max(56.2,65.3);
但输出仍为整型:56<65
,这是因为我们前面提到过,ming对象的类型为Person<int,string>
,所以它会将两个浮点数进行强制类型转换,变为整型,再进行函数的执行
类模板与继承
类模板相关的继承有四种:
- 普通类继承类模板
- 类模板继承普通类
- 类模板继承类模板
- 类模板继承由继承模板参数指定的基类
1、普通类继承类模板
template<class T>
class one
{
T data;
};
class sub:public one<int>
{
};
2、类模板继承普通类
class one
{
};
template<class T>
class sub:public one
{
T data;
};
3、类模板继承类模板
template<class T>
class one
{
T data1;
};
template<class T1,class T2>
class sub:public one<T1>
{
T2 data2;
};
4、模板类继承模板参数给出的基类——继承哪个基类由模板参数决定
#include<iostream>
using namespace std;
class BaseA{
public:
BaseA(){cout<<"BaseA founed"<<endl;}
};
class BaseB{
public:
BaseB(){cout<<"BaseB founed"<<endl;}
};
template<typename T, int rows>
class BaseC{
private:
T data;
public:
BaseC():data(rows){
cout<<"BaseC founed "<< data << endl;}
};
template<class T>
class Derived:public T{
public:
Derived():T(){cout<<"Derived founed"<<endl;}
};
void main()
{
Derived<Base A> x;// BaseA作为基类
Derived<Base B> y;// BaseB作为基类
Derived<Base C<int, 3> > z; // BaseC<int,3>作为基类
}
总结
模板在C++中是非常重要的,它是实现代码重用机制的一种工具;通过模板,我们可以进行泛型编程,它可以实现类型参数化,从而实现了真正的代码可重用性。