【c++】模板
一.模板的概念
1. 什么是模板
2. 模板的作用
3. 模板的语法
二.函数模板
三.类模板
四.模板的编译类型
参考:《c++从入门到精通》 人民邮电出本社
在制作ppt时,我们可以使用模板,其中已为我们提供了基本元素,只需要自己填入相应数据即可。在C++中,也有这样的模板,只需在使用前填入自己需要的数据类型即可。
模板是c++语音中一个相对较新的重要特性,是实现代码重用机制的一种工具。模板可以分为两类:函数模板和类模板。本节介绍模板的概念、定义以及使用方法,以便后续能正确使用C++系统日渐庞大的标准模板类库,以及定义自己的模板类和模板函数,进行更大规模的软件开发。
一. 模板的概念
1. 什么是模板
模板是实现代码重用机制的一种工具,可以实现类型参数化,即把类型定义为参数,从而实现代码的可重用性。
利用模板,我们可以把功能相似、仅数据类型不同的函数或类设计为通用的函数模板或类模板,提供给用户。
模板是“泛型编程”的基础,所谓泛型编程就是用独立于任何特定类型的方式编写代码。所以,类是对象的抽象,而模板又是类的抽象,用模板能定义出具体的类(类是类模板的抽象)。
2. 模板的作用
之前我们学过函数的重载,以便于实现同名函数对不同类型参数的区分。例如:max()函数的重载,
每个要比较的类型都需要单独定义,非常麻烦。而,每个max()的功能都相同,函数体也相同,仅仅是参数类型不同,那能不能只写一套代码,对于任一类型T的两个对象a,b,max(a,b)总能使得编译系统理解其意义并实现呢?
可以!于是c++引入了模板机制。
模板的作用:
(1)实现代码的可重用性
(2)模板能减少源代码量,并提高代码的机动性,而不会降低类型安全。
3. 模板的语法
(一)函数模板的一般形式:
template <class T1 ,class T2 ,typenameT3 ……> 返回类型 函数名(函数形参表)
{
// 定义函数体
}
注意:
(1)模板形参表不能为空。模板形参可分为:模板类型参数(代表一种类型)和模板非类型参数(代表一个常量表达式)。
(2)模板非类型参数的类型必须是下面的一种:整型或枚举、指针类型、引用类型
其他类型不允许作为模板的非参数类型使用。
关键字typename和class有何区别呢?
在模板定义中,二者意义相同,可以互换使用,甚至可以在同一模板形参表中同时存在。
(二)类模板的一般形式:
template <class T1>
class 类名
{
// 类定义
}
注意:
1.Template是声明各模板的关键字,表示声明是一个模板。
2. 模板参数应是抽象化的结果(class,typename),而不应是具体的(int\float等)。
3. 类模板中,成员函数不能声明为虚函数。
二.函数模板
函数模板的一般形式:
template <class T1 ,class T2 ,typenameT3 ……> 返回类型 函数名(函数形参表)
{
// 定义函数体
}
【函数模板实例】实现max()函数
//函数模板.cpp
#include<iostream>
using namespace std;
template <class T>
T _max(T a,T b)
{
return (a>b)? a:b ;
}
int main(int argc,char *argv[])
{
int a1=3,a2=5;
double b1=3.5,b2=1.5;
cout<<"函数模板"<<endl;
cout<<"max(3, 5)="<<_max(a1,a2)<<endl;
cout<<"max(3.5, 1.5)="<<_max(b1,b2)<<endl;
return 0;
}
运行结果:
【实例分析】
(1) 程序中定义的template <class T> T _max(T a,T b) ,这并不是一个实实在在的函数(类似于虚函数),编译器不会为其产生任何可执行代码。该定义只是对函数的描述,表示它每次都能单独处理在类型形参表中说明的数据类型。
(2) 调用max(a1,a2)时,编译器发现调用max(a1,a2),则根据实参类型int,先生成一个重载函数:
int max(int a,int b) {return a>b? a:b ;}
该重载函数的定义体跟函数模板的相同,而形参以实参类型为依据。(所以,函数模板的内核是函数重载)。该重载函数称为模板函数,由函数模板生成模板函数的过程称为函数模板的实例化。
(3) 可用下图表示函数模板的实例化过程:
注意:max(a,b)中的a.b为同一类型,若一个为int,一个为double,则调用会出错。
三.类模板
类模板的一般形式:
template <class T1>
class 类名
{
// 类定义
}
【类模板实例】不同类型参数的构造函数
//类模板.cpp
#include<iostream>
using namespace std;
template <class T1,class T2>
class myClass
{
private:
T1 a;
T2 b;
public:
myClass(T1 x,T2 y)
{
a=x;
b=y;
}
void show()
{
cout<<"a="<<a<<" b="<<b<<endl;
}
};
int main(int argc,char *argv[])
{
cout<<"类模板"<<endl;
myClass<int,int> c1(1,2);
c1.show();
myClass<int,double> c2(5, 2.3);
c2.show();
myClass<char,int> c3('z',3);
c3.show();
return 0;
}
运行结果:
【实例分析】
类模板与模板类的关系?
类模板是模板的定义,它属于一种模板,是抽象的,相当于一种数据类型。而模板类实实在在的类,它是由类模板定义的实例类。所以类模板是模板类的抽象,而模板类是类模板的实例。
类模板与函数模板的区别?
函数模板的实例化是由编译程序在处理函数调用时自动完成的,与函数模板不同的是,类模板的实例化必须由程序员在程序中显式地指定。
例:函数模板的实例化:inta1=3,a2=5; max(a1,a2);
类模板的实例化:myClass<int,int>c1(1,2);
注意:类模板的实例化时,已经标明了实参类型<int ,int >
四.模板的编译类型
标准c++为编译模板代码定义了两种模型:
(1) 包含编译模型
(2) 分离编译模型
在这两种模型中,构造程序的方式基本相同,类定义和函数声明都放在头文件(.h文件)中,而函数定义和成员定义则放在源文件(.cpp文件)中。
两种模型的不同之处,主要在于编译器怎样使用来自源文件的定义。
(1)包含编译模型
模板的编译有什么特殊之处?
一般来说,调用函数时,编译器只需要看到函数的声明或类的定义。但模板则不同,当编译器看到模板定义的时候,并不会立即产生代码,只有在调用了函数模板或类模板的对象时,才产生特定类型的模板实例。因此要进行实例化,编译器必须能够访问定义模板的源代码。当调用函数模板或类模板的成员函数时,编译器需要那些通常放在源代码中的代码。
例如,将前面的max()函数模板进行改写。
在头文件max.h中进行函数模板的声明:(注意添加 #include"max.cpp")
#ifndef MAX_H
#define MAX_H
template<class T> T max(T a,T b); //函数模板声明
#include"max.cpp"
#endif // MAX_H
在max.cpp中进行函数模板定义
template <class T> T max_(T a,T b)
{
return a>b? a:b;
}
在main()函数中:
#include <iostream>
#include"max.h"
using namespace std;
int main()
{
cout<<"包含编译模型"<<endl;
cout<<"max_(1,2)="<<max_(1,2)<<endl;
return 0;
}
运行结果:
注意:
“包含编译模型”并不是类模板的声明和定义放在一个头文件里;
“分离编译模型”也不是将声明和定义分开。
(2) 分离编译模型
在分离编译模型中,编译器会为我们跟踪相关的模板定义。但是,必须让编译器知道要记住给定的模板定义,怎么办?可以使用export关键字来实现。
Export关键字有什么用?
例如,一般在函数模板的定义中,可通过在关键字template之前包含export关键字,以指明函数模板为导出的。
注意:在程序中,一个函数模板只能被定义为export一次。//仅在定义时export,声明时不用export。
分离编译模式使得我们能够更好的把类模板的接口同其实现分离开,它使得我们能够这样来组织程序:把类模板的接口放在头文件中,而把具体实现放在源文件中。
【编程实例】
Max.h中: //定义类模板 myClass
#ifndef MAX_H
#define MAX_H
#include <iostream>
template <class T> //T max_(T a,T b)
class myClass
{
private:
T a;
T b;
public:
myClass(T x,T y)
{
a=x;
b=y;
}
void show()
{
std::cout<<"a="<<a<<" b="<<b<<std::endl;
}
};
#endif // MAX_H
Max.cpp中://在cpp中export类模板
export template<class T> class myClass;
#include"max.h"
//接下来是类成员的定义
Main.cpp中:
#include <iostream>
#include"max.h"
using namespace std;
int main()
{
cout<<"分离编译模型"<<endl;
myClass<int> aa(1,2);
aa.show();
return 0;
}
运行结果:
------------------------------------------- END -------------------------------------