前文
本文主要是讲解一下C++中的利器——模板,相信铁子们在学完这一节后,写代码会更加的得心应手,更加的顺畅。
一,泛型编程
想要学习模板,我们要先了解为什么需要模板,我们可以看看下面这个程序。
int add(int& a, int& b)
{
return a + b;
}
double add(double& a, double& b)
{
return a + b;
}
看看上面的程序,通过函数重载,我们实现了不同类型的add函数,但是否有些麻烦呢,这些函数只是类型不同,而每增加一个新类型我们就要在写一个函数,而且一个出错,可能所有的函数都出错。
而我们C++的老祖宗也想到了这个问题,于是 模板就应运而生了。
通过 模板来实现我们的需求可以称为 泛型编程。
泛型编程: 编写与类型无关的通用代码,是代码复用的一种手段。模型是泛型编程的基础
二,函数模板
2.1 函数模板的概念
函数模板代表了一个函数家族,该 函数模板与类型无关,在 使用时被参数化,根据 实参类型 产生函数的特定类型版本。
2.2函数模板格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
template<typename A>
A add(A& a, A& b)
{
return a + b;
}
注意:typename是用来定义模板参数关键字,也可以使用 class(切记:不能使用struct代替class)
2.3 函数模板的原理
函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后 只是一个对函数功能框架的描述,当它具体执行时, 将根据传递的实际参数决定其功能。 所以其实就是将我们重复做的事情交给了编译器,当然要是怕累着编译器也可以自己实现,haha。
template<typename A>
A add(A& a, A& b)
{
return a + b;
}
int main()
{
int a = 1, b = 2;
add(a, b);
double c = 3.3, d = 4.4;
add(c, d);
return 0;
}
如以上程序调用方式如下
当我们调试的时候调到反汇编,我们可以发现int型的和double型所调用的函数是不一样的,由此我们也可以论证, 编译器根据模板创建出对应函数以供使用,在这个过程中模板起到了一个蓝图的作用,而具体的建造材料则由我们传过去的参数决定。
而这个建造的过程一般是在 编译器编译阶段,对于函数模板的使用 ,编译器会根据传入的实参类型来推演出对应类型的函数以供使用。比如:当用double类型使用函数模板add时,编译器通过对参数类型的推演,将A确定为double型创造函数。
2.4 函数模板的实例化
用不同类型的参数调用函数模板,称为函数模板的 实例化。模板参数实例化分为: 隐式实例化和显示实例化
1.隐式实例化:让编译器根据实参推演模板参数的实际类型
template<typename A>
A add(A& a, A& b)
{
return a + b;
}
int main()
{
//下面两个是比较典型的隐式实例化
int i1 = 1, i2 = 2;
add(i1, i2);
double d1 = 3.3, d2 = 4.4;
add(d1, d2);
//那么下面这种情况呢
//add(i1,d1) add(i2,d2)
//这种情况下,编译器会根据实参i1将A推演为int
//通过实参d1将A推演为double型
//此时编译器不知道用那个,就会报错
//那么怎么解决这种情况呢
//1.强制转化
add(i1, (int)d1);
add((double)i2, d2);
//2.显示实例化
return 0;
}
2.显示实例化:在函数名后的<>中指定模板参数的实际类型
2.5 模板参数的匹配规则
一个非模板函数可以和一个同名函数模板同时出现,而且该函数模板还可以被实例化成这个非模板函数
int add(int& a, int& b)
{
return a + b;
}
template<typename A>
A add(A& a, A& b)
{
return a + b;
}
int main()
{
add(1, 2);
//此时会优先调用非模板函数
//因为跳过模板实例化的过程效率更高
add(int)(1, 2);//通过实例化模板,
//也可以实例化出这个非模板函数
return 0;
}
对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
int add(int& a, int& b)
{
return a + b;
}
template<typename A>
A1 add(A1& a, A2& b)
{
return a + b;
}
int main()
{
add(1, 2);//完全匹配非模板函数
//不会在进行模板实例化,而是直接选择非模板函数
add(1, 1.1);//与非模板函数不匹配,
//模板函数会根据参数生成更加匹配的函数
return 0;
}
模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
三,类模板
3.1 类模板的定义格式
定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
template<class A>
class Stack
{
public:
Stack(int capaicty = 4)
{
_a = new A[capaicty];
_size = 0;
_capaicty = capaicty;
}
~Stack()
{
delete[] _a;
_size = _capaicty = 0;
}
private:
A* _a;
int _size;
int _capaicty;
};
int main()
{
//通过类模板,我们可以同时创建int和double类型的栈
Stack <int>st1;
Stack <double>st2;
return 0;
}
3.2 类模板的实例化
类模板实例化与函数模板实例化不同, 类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
总结
学习过模板后,C++的学习也会进入一段蜜月期,因为之后我们会接着学习stl库,这将大大提高我们C++代码的编写效率。