函数模板,顾名思义,是在生成函数时依照的模板。
有时,我们需要对不同的数据类型做同样的函数操作。
比如:分别对一个int类型数 和 一个double类型数求平方。
这时,虽然都是同样的求平方操作(函数体内代码一样),但是我们必须要编写两个不同的函数,因为处理int类型的函数的参数和返回值类型都应该是int,而处理double类型的函数的参数和返回值都应该是double。
如下:函数体内操作代码一样,设计为重载函数,用相同的函数名,但是参数类型和返回值类型却都不一样,函数需要定义两次。
int Square(int a) //求int类型变量的平方值 {
int b;b=a*a; return b; } double Square(doublea) //求double类型变量的平方值 {
double b;b=a*a; return b; }
此时,如果使用函数模板就可以省去多次重复定义函数的麻烦,消除冗余代码 ,提升代码重用性。
函数模板的定义形式:
template<模板参数表>
类型名 函数名 (参数表)
{
函数体定义
}
对于模板参数表:由 “class”或“typename ”加上 “类型说明符” 组成。(模板参数表还可以为“template<参数表>class” 加上 “类型说明符”,表示接收一个类模板作为参数,关于类模板的说明在后面)
模板参数表 用来指定函数模板的形参类型、返回值类型以及函数中的局部变量类型。
如以上Square函数的函数模板如下:
template<typename T> T Square(T a) {
T b;b=a*a; return b; }
此时,再分别计算int类型和double类型变量的平方时,我们不再需要手动书写两个函数,只需按如下方式执行即可。
int main() { int m=2; double n= 3.0; cout<<Square(m)<<endl; couta<<Square(n)<<endl; return 0; }
在上述Square()被调用时,编译器从实参的类型(如int)推导出函数模板的参数类型(int),然后编译器将依据函数模板来生成一个如下函数:
(在调用Square(m)时,执行的函数实际上是int Square(int a)这个函数,同理调用Square(n)时,实际上执行的是double Square(double a)。)
int Square(int a) { int b;b=a*a; return b; }
这一个过程我们称之为,函数模板的实例化。函数模板实例化的过程用户是不用关心的。
关于函数模板有几点需要注意:
1.函数模板本身在编译时不会生成任何目标代码,只有函数模板生成的实例才生成目标代码。
2.如果函数模板被多个文件引用,函数模板的函数体也应该放在头文件里,而不能只放函数模板的声明。
3.函数指针不能指向函数模板本身,只能指向函数模板的实例。
类模板,与函数模板类似,是生成类时依照的模板。
类模板声明语法格式:
template<模板参数表>
class 类名
{
类成员声明
}
同理,模板参数表用于影响类成员(函数成员及数据成员)的中的数据类型。
例:
template<class T> class printer{ //一个打印机类模板,可以接收并打印给定数据类型 private: T a; bool b; //用来标记是否有可给出内容 public: T& prin(); //给出要打印内容 void get(const T& x); //获得要打印内容 };
在类模板外定义其函数成员的格式如下:
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
对应上述类模板有:
template<class T> T& printer<T>::prin(){ if(b) return a; } template<class T> void printer<T>::get(const T& x){ a=x; b=true; }
类模板同样拥有实例化的过程。
使用一个类模板来建立对象方法:
模板名<模板参数表>对象名1,对象名2,...,对象名n;
建立对象的过程:首先模板实例化生成具体的类,然后类再实例化出一个个对象。
如 printer<int>p1,p2; 建立了两个可以获得并给出int类型数据的打印机p1和p2。
int main(){ printer<int>p1,p2; p1.get(1); p2.get(2); cout<<"p1要打印的是"<<p1.prin()<<endl; cout<<"p2要打印的是"<<p2.prin()<<endl; return 0; }
通过模板可以实现参数化多态性(就是将处理的对象的类型参数化,使得一段程序可以处理不同类型的对象)。
以上。