欢迎来到繁星的CSDN。本期内容主要包括模板template。
目录
一、什么是模板?
模板,工业生产上用以注塑、吹塑、挤出、压铸或锻压成型、冶炼、冲压等方法得到所需产品的各种模子和工具。 简而言之,模板是用来制作成型物品的工具,这种工具由各种零件构成,不同的模板由不同的零件构成。它主要通过所成型材料物理状态的改变来实现物品外形的加工。素有“工业之母”的称号。
不得不说,模板是工业上是堪比轮子的发明。
而C++中,模板也正是我们“量产”代码,复用代码的关键。
template<class T>
void swap(T& a, T& b) {
T tmp=a;
a = b;
b = tmp;
}
如图,便是一个简单的swap函数,它支持任何两个同一类型的变量进行交换。
(这里的swap改为大写的原因是,C++<iostream>库里有相同的swap函数,为了不影响演示,所以改了函数名字)
但当我们输入的a和b类型不同的时候,系统会报错。
这个问题先按下不表,等讲完函数模板,也就清晰了。
二、函数模板
在刚刚的错误列表中,我们发现了函数模板字样。而事实上,模板分为函数模板和类模板。这一部分先叙述函数模板,因为函数模板往往是类模板的一个组成部分。
模板的定义方式
template的相关内容当然没有这么少。
template<class T1, class T2,.....>
(返回值类型)(函数名)(形参){
//write code here
}
模板关键字template可以同时定义多个虚拟类型的参数,T1,T2等等,程序员可以在这里写上百个参数,但在后面的模板里,你必须用到这些参数,否则编译器会报错。
定义虚拟类型的关键字是typename或者class,这两种都可以且可以混用,但不能用struct!
而对于同一个虚拟类型参数而言,编译器会在编译使其实例化的时候,让同一个虚拟参数全部编译为某一类型。
所以,如果将代码按如下改造,便可以使得模板更灵活。
template<class T1,class T2>
void Swap(T1& a,T2& b){
T1 tmp=a;
a=b;
b=tmp;
}
因为T1和T2可以不为同一类型,也可以是同一类型,所以在语法层面上来说,没有错误。
但很抱歉的是,如果这么写,无法得到真正的转换。(尽管这是函数的问题,而非模板)
模板的实例化(确定参数的类型)
模板的实例化分为两类:隐式实例化,显示实例化。
隐式实例化
隐式实例化我们实际上已经演示过了,上图的Swap就是隐式实例化。
在函数编译的时候,编译器会通过我们传入的参数的类型,来“猜”对应的虚拟类型是什么。
如果发现猜不到类型(如对着同一个虚拟类型传入了两个不同类型的参数),编译器会开始查看是否有其他的重载函数。因为除了模板,普通函数也是可以重载的。
这两步都做完,如果还找不到,那么编译器就会报错。
显式实例化
template<class T1,class T2>
void Swap(T1& a, T2& b) {
T1 tmp=a;
a = b;
b = tmp;
}
int main() {
int a = 10;
double b = 9.9;
Swap<int,double>(a, b);
cout << "a:" << a << endl;
cout << "b:" << b << endl;
return 0;
}
观察调用Swap的时候,我们在Swap函数后加入了<int,double>,代表第一个参数是int,第二个参数是double(但仍然需要定义两个虚拟类型)
如果只需要一个参数类型,也可以这么写:
Swap<int>(a,b);
这就意味着将b强制类型转换为int。
当显式实例化不成功,编译器会尝试自己匹配类型(隐式实例化)。
实例化顺序
1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
最容易的理解方式是:
模板是参数相同的同名函数的一个总和,这意味着普通函数,也可能是某一模板的实例化。
模板实例化出一个函数是需要消耗资源的,编译器会优先使用已有的模板函数,但如果在匹配上,模板实例化出的函数更匹配,那么就会用模板。这和我们之前提到的引用返回还是传值返回的原则很像,先达成目的,再考虑效率。
需要注意的一点是:
模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
三、类模板和模板类
类模板,本质还是模板,只不过是某一种类的模板,因而称为类模板。其实例化产生的类,称为模板类。这两个名字经常容易混淆,实际上只需要看后缀,一个本质是模板,一个本质是类。
类模板的定义格式如下:
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
需要注意的是,类模板中函数在类外定义时,需要加模板参数列表。(声明与定义分离的情况),所以在练习的时候,为了避免不必要的麻烦,尽量在类内定义。工作中可能因为需要时常维护,导致需要分离定义。
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
if(_pData)
delete[] _pData;
_size = _capacity = 0;
}
类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;
(这里大写的原因,仍然是因为vector在C++中有特殊含义,后续会讲到)。
本篇内容到此结束,谢谢大家的观看!
觉得写的还不错的可以点点关注,收藏和赞,一键三连。
我们下期再见~
往期栏目: