系列文章目录
C plus plus ——【模板应用】
文章目录
- 系列文章目录
- 前言
- 一、函数模板
- 1.1、函数模板的定义
- 1.2、函数模板的作用
- 1.3、重载函数模板
- 二、类模板
- 2.1、类模板的定义与声明
- 2.2、简单类模板
- 2.3、默认模板参数
- 2.4、为具体类型的参数提供默认值
- 三、总结
前言
模板是C++语言的高级特性,分为函数模板和类模板两大类。模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现大大方便了大规模软件开发。
一、函数模板
函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
1.1、函数模板的定义
函数模板定义的一般形式如下:
template <类型形式参数表>
返回类型 函数名(形式参数表)
{
//函数体
}
template 为关键字,表示定义一个模板;尖括号“<>” 表示模板参数,模板参数主要有两种,一种是模型类型参数,另一种是模板非类型参数。模板类型参数使用关键字class 或 typedef 开始,其后是一个用户定义的合法标识符。模板非类型参数与普通参数定义相同,通常为一个常数。
可以将声明函数模板分成template部分和函数名部分。例如:
template<class T>
void fun(T t)
{
//函数实现
}
定义一个求和的函数模板,例如:
template <class type> //定义一个模板类型
type Sum(type xvar,type yvar) //定义函数模板
{
return xvar+yvar;
}
在定义完函数模板之后,需要在程序中调用函数模板。下面的代码演示了Sum函数的调用。
int iret = Sum(10,20); //实现两个整数的相加
double dret = Sum(10.5,20.5); //实现两个实数的相加
如果采用如下的形式调用Sum函数模板,将会出现错误。
int iret = Sum(10.5,20); //错误的调用
double dret = Sum(10,20.5); //错误的调用
上述代码中为函数模板传递了两个类型不同的参数,编译器产生了歧义。如果用户在调用函数模块时显示标识模板类型,就不会出现错误了。例如:
int iret = Sum<int>(10.5,20); //正确地调用函数模板
double dret = Sum<double>(10,20.5) //正确地调用函数模板
用函数模板生成实际可执行的函数又称为模板函数。函数模板与模板函数不是一个概念。从本质上讲,函数模板是一个“框架”,它不是真正可以编译生成代码的程序,而模板函数是把函数模板中的类型参数实例化后生成的函数,它和普通函数本质是相同的,可以生成可执行代码。
1.2、函数模板的作用
假设求两个函数之中最大者,如果想求整型数和实型数需要定义以下两个函数:
int max(int a,int b)
{
return a>b?a:b; //返回最大值
}
float max(float a,float b)
{
return a>b?a:b; //返回最大值
}
可以使用函数模板以及#define 宏定义实现一个max 函数来完成既求整型数之间最大者又求实型数之间的最大者。
#define 宏定义可以在预编译期对代码进行替换。例如:
#define max(a,b) ((a)>(b)?(a):(b))
上述代码可以求整型数最大值和实型数最大值。但宏定义#define 只是进行简单替换,它无法对类型进行检查,有时计算结果可能不是预计的。例如:
#include <iostream>
#include <iomanip>
using namespace std;
#define max(a,b)((a)>(b)?(a):(b))
int main(int argc,char* argv[])
{
int m=0,n=0;
cout<<max(m,++n)<<endl;
cout<<m<<setw(2)<<endl;
return 0;
}
程序运行结果如下:
程序运行的预期结果应该是1和0,但是实际是2和0,这是因为宏替换之后“++n”被执行了两次,因此n的值是2不是1。
宏是预编译指令,很难调试,无法单步进入宏的代码中。模板函数和#define 宏定义相似,但模板函数是用模板实例化得到的函数,它与普通函数没有本质区别,可以重载模板函数。
使用模板求最大值的代码如下:
template <class Type>
type max(Type a,Type b)
{
if(a>b)
return a;
else
return b;
}
调用模板函数max可以正确计算整型数和实型数的最大值。例如:
cout<<"最大值:"<<max(10,1)<<endl;
cout<<"最大值:"<<max(200.05,100.4)<<endl;
使用数组作为模板参数。
#include <iostream>
using namespace std;
template <class type,int len> //定义一个模板类型
type Max(type array[len])
{
type ret = array[0]; //定义一个变量
for(int i=1; i<len;i++) //遍历数组元素
{
ret = (ret>array[i])?ret:array[i]; //比较数组元素大小
}
return ret; //返回最大值
}
int main(int argc,char* argv[])
{
int array[5] = {1,2,3,4,5}; //定义一个整型数组
int iret = Max<int,5>(array); //调用函数模板Max
cout<<iret<<endl;
double dest[3] = {10.5,11.2,9.8}; //定义实数数组
double dret = Max<double,3>(dest); //调用函数模板
cout<<dret<<endl;
}
程序运行结果如下图所示:
程序中定义一个函数模板Max,用来求数组中元素的最大值,其中模板参数使用模板类型参数type和模板非类型参数len,参数type声明了数组中的元素类型,参数len声明了数组中的元素个数,给定数组元素后,程序将数组中的最大值输出。
1.3、重载函数模板
整型数和实型数编译器可以直接进行比较,所以使用函数模板后也可以直接进行比较。重载函数模板可以用来使字符指针指向的字符串进行比较。
#include <iostream>
#include <string>
using namespace std;
template <class Type>
Type Tmin(Type a,Type b) //定义函数模板
{
if(a<b)
return a;
else
return b;
}
char *Tmin(char *a,char *b) //重载函数模板
{
if(strcmp(a,b))
return b;
else
return a;
}
int main(int argc ,char* argv[])
{
cout<<"Minimum:"<<min(10,1)<<endl;
cout<<"Minimum:"<<min('a','b')<<endl;
cout<<"Minimum:"<<min("hi","mr")<<endl;
return 0;
}
程序运行结果如下图所示:
程序在重载的函数模板Tmin的实现,使用strcmp库函数来完成字符串的比较,此时使用Tmin函数可以比较整型数据、实型数据、字符数据和字符串数据。
二、类模板
使用template关键字不但可以定义函数模板,也可以定义类模板。类模板代表一族类,是用来描述通用数据类型或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意数据类型。类模板可以说是用类生成类,减少了类的定义数量。
2.1、类模板的定义与声明
类模板的一般定义形式如下:
template <类型形式参数表>
class 类模板名
{
...//类模板体
};
类模板成员函数的定义如下:
template <类型形式参数表>
返回类型 类模板名 <类型名表>::成员函数名(形式参数列表)
{
... //函数体
}
template 是关键字,类型形式参数表与函数模板定义相同。类模板的成员函数定义时的类模板名与类模板定义时要一致,类模板不是一个真实的类,需要重新生成类,生成类的形式如下:
类模板名<类型实在参数表>
用新生成的类定义对象的形式如下:
类模板名<类型实在参数表> 对象名
其中类型实在参数表应与该类模板中的类型形式参数表匹配。用类模板生成的类称为模板类。类模板和模板类不是同一个概念,类模板是模板的定义,不是真实的类,定义中要用到类型参数;模板类本质上与普通的类相同,它是类模板的类型参数实例化之后得到的类。
定义一个容器的类模板,代码如下:
template <class Type>
class Container
{
Type tltem;
public:
Container()
{
};
void begin(const Type&tNew);
void end(const Type&tNew);
void insert(const Type&tNew);
void empty(const Type&tNew);
};
和普通类一样,需要对类模板成员函数进行定义,代码如下:
void Container<type>::begin(const Type&tNew) //容器的第一个元素
{
tltem = tNew;
}
void Container<type>::end(const Type&tNew) //容器的最后一个元素
{
tltem = tNew;
}
void Container<type>::empty(const Type&tNew) //清空容器
{
tltem = tNew;
}
将模板类的参数设置为整型,然后用模板类声明对象,代码如下:
Container<int> myContainer; //声明Container<int>类对象
声明对象后,就可以调用类成员函数,代码如下:
int i = 10;
myContainer.insert(i);
在类模板定义中,类型形式参数表中的参数也可以是其他类模板。例如:
template<template<class A> class B>
class CBase
{
private:
B<int> m_n;
}
类模板也可以进行继承。例如:
template<class T>
class CDerived public T
{
public:
CDrived();
};
template<class T>
CDerived<T>::CDerived:T()
{
cout<<""<<endl;
}
void main()
{
CDerived<CBase1>D1;
}
T是一个类,CDerived继承自该类,CDerived 可以对类T进行扩展。
2.2、简单类模板
类模板中的类型形式参数表可以在执行时指定,也可以在定义类模板时指定。简单类模板如下:
简单类模板。
#include<iostream>
using namespace std;
template<class T1 ,class T2>
class MyTemplate
{
T1 t1;
T2 t2;
public:
MyTemplate(T1 tt1,T2 tt2)
{
t1 = tt1;
t2 = tt2;
}
void display()
{
cout<<t1<<' '<<t2<<endl;
}
};
int main()
{
int a=123;
double b=3.1415;
MyTemplate<int ,double>mt1(a,b);
MyTemplate<int,int > mt2(a,b);
mt1.display();
mt2.display();
return 0;
}
程序运行结果如下图所示:
2.3、默认模板参数
默认模板参数是类模板中由默认的数据类型作参数,在模板定义时还可以为默认的数据类型声明变量,并且为变量赋值。为具体类型的参数提供默认值实例如下:
#include<iostream>
using namespace std;
template<class T1,class T2 = int>
class MyTemplate
{
T1 t1;
T2 t2;
public:
MyTemplate(T1 tt1,T2 tt2)
{
t1 = tt1;
t2 = tt2;
}
void display()
{
cout<<t1<<' '<<t2<<endl;
}
};
int main(int argc ,char* argv[])
{
int a = 123;
double b = 3.1415;
MyTemplate<int ,double> mt1(a,b);
MyTemplate<int> mt2(a,b);
mt1.display();
mt2.display();
return 0;
}
程序运行结果如下图所示:
2.4、为具体类型的参数提供默认值
默认模板参数是类模板中由默认的数据类型作参数,在模板定义时还可以为默认的数据类型声明变量,并且为变量赋值。
#include<iostream>
using namespace std;
template<class T1,class T2,int num = 10>
class MyTemplate
{
T1 t1;
T2 t2;
public:
MyTemplate(T1 tt1,T2 tt2)
{
t1 = tt1+num,t2 = tt2+num;
}
void display()
{
cout<<t1<<' '<<t2<<endl;
}
};
int main(int argc ,char* argv[])
{
int a = 123;
double b = 3.1415;
MyTemplate<int ,double> mt1(a,b);
MyTemplate<int ,double,100> mt2(a,b);
mt1.display();
mt2.display();
return 0;
}
程序运行结果如下图所示:
三、总结
模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现大大方便了大规模软件开发。