模板简介
*C++*最重要的特性之一就是代码的重用,为了实现代码重用,其通用性就显得尤为重要。通用代码应不受数据类型的影响,并且可以自动适应数据类型的变化。而模板,就是实现它(参数多态性)的C++程序设计方法。
前言
模板按实现可分为函数模板与类模板。本篇博客将分别给出其语法、实现与注释,并将其实例化展示应用。
一、函数模板
1、引子
先请看两个求绝对值函数:
#include <iostream>
using namespace std;
int abs(int num){
return num>0?num:-num;
}
double abs(double num){
return num>0?num:-num;
}
int main(){
int a=0;
double b=-9.8;
cout<<abs(b)<<endl;
cout<<abs(a)<<endl;
return 0;
}
输出结果:
// 这儿有个小秘密,编译器会为形参自动匹配最合适的重载函数!
两个函数只有参数类型不同,而功能完全一样。若能用一段通用代码来适应不同的数据类型,便会使代码的重用性大大提高!同时,减少代码膨胀。
2、函数模板语法
函数模板的定义形式:
template<模板参数表>
类型名 函数名(形参列表)
{
statement;
}
类型名即返回值类型,需要时可加上一系列修饰关键字。
模板参数表中参数由逗号分隔各个参数,如:
template Function <class T,typename M>
其中,标识符T一般指接收一个类型参数,M可接受包括内置类型在内的任何类型参数,且,可接受一个常量。
3、模板示例实现:
#include <iostream>
using namespace std;
template<typename T>
T abs(T num){
return num>0?num:-num;
}
int main(){
int a=0;
double b=-9.8;
cout<<abs(b)<<endl;
cout<<abs(a)<<endl;
return 0;
}
输出结果同上文。
可以看到,main函数中的函数调用方法并没有改变,却减少了文本中的代码量。
4、注意
需要注意到,在编译器编译模板函数时,并不生成任何目标代码!而是等到主函数调用 abs()函数 时,即当类型参数的含义明确后,编译器将以模板函数为样例,分别生成两个函数。 这个过程也叫做函数模板的 “实例化”。
并且,函数指针也只能指向模板的实例,而不能指向无目标代码的模板本身!
此外,template<参数表>class 标识符,指明可以接受一个类模板作为参数。
二、类模板
1.引入
类是对一组对象的共性的抽象,而类族与继承派生技术使得同类之间的对象个性分明。类模板可以为类定义一种模式,使得类中某些数据成员、函数成员的参数、返回值或自定义数据成员能取任意类型!
由于类模板需要一种或多种类型参数,所以类模板也常常称为参数化类。
2.类模板语法声明
类模板的语法形式:
template<模板参数表>
class 类名
{
statements;
}
其中,statements(类成员声明)的方法与普通类的定义在除了各个数据成员中通常要用到模板的类型参数 T (注意,这里的T只是一种泛称,可以取除关键字外的任何名字!) 之外,几乎相同!
如果需要在类模板以外定义定义其成员函数,则需要用到以下语法:
template<模板参数列表>
类型名 类名<模板参数标识符列表>::函数名 (参数表);
使用一个模板类来建立对象时,以vector容器(亦为一个类模板)为例:
vector v1,v2…Vn;
抽象后:
模板名<模板参数表> 对象名1,对象名2…对象名n;
3、类模板应用举例
例:希望构建一个实现任意类型数据存取的类模板Store,然后通过具体数据类型参数对类模板实例化生成类,再通过类的实例化生成对象 s1、s2、s3和d。不过,类模板的实例化过程在程序中是隐式的!
源代码如下:
#include <iostream>
#include <cstdlib> //exit()函数接口
using namespace std;
struct Student{
int id; //学号
float avg; //均分
};
template<class T> //类模板
class Store{
private:
T item; //item用于存放任意类型的数据
bool have_value; //have_value用来标记是否存放数据
public:
Store(); //默认构造函数(无参)
T &getElem(); //外部获取私有成员 T接口
void putElem(const T &x); //外部私有成员赋值接口
};
//以下实现各成员函数
template<class T> //默认构造函数实现
Store<T>::Store():have_value(0){};
template<class T>
T &Store<T>::getElem(){
if(!have_value){ //如果试图提取未初始化的数据,则输出提示串并终止程序
cout<<" No item present!"<<endl;
exit(1); //使程序完全退出,返回操作系统、参数可以可以用来表示被操作系统接收
}
return item;
}
template<class T>
void Store<T>::putElem(const T &x){
have_value=true; //存入数据,改变have_value的状态
item=x;
}
int main()
{
Store<int>s1,s2;
s1.putElem(3);
s2.putElem(-8);
cout<<s1.getElem()<<" "<<s2.getElem()<<endl;
Student z={2000,45};
Store<Student>s3;
s3.putElem(z);
cout<<"The student's id is"<<s3.getElem().id<<endl;
Store<double>num;
cout<<"Retrieving object num...";
cout<<num.getElem()<<endl; //由于num为类内成员,未经初始化,在函数getElem()执行的过程中导致程序终止!
return 0;
}
运行结果:
有了这个框架,你可以根据自己的独特需求与想法在其中增加自己想要的功能,如对存储数据的增、删、改、查 等等。
三、总结
C++模板的简单介绍就先告一段落。
STL中有着大量用模板元编程,亦称泛型编程编写的成品。而模板元编程的根就是模板。模板的使命就是为自动代码生成提供方便,减少代码膨胀,提高程序员生产效率。
私以为可用一言以蔽之:用一般化代码生成个性化代码的方法! 而工作方式上就是减少了编译期的工作量,而加大了运行期的工作量。模板是用否?仁者见仁智者见智,根据场景再谈吧。
如果此篇博客对您有些许帮助,还请留下您的鼓励与支持。感激不胜!