目录
前言
早在几千年前,古人发明了活字印刷术,而活字印刷术也与我们今天的模板类似,C++的模板大体可分为两类,分别为函数模板与类模板,在正式了解模板之前,我们首先的了解函数重载的概念,如果对函数重载不了解的,可移步初识C++ 中了解关于函数重载的概念后,再来学习模板相关知识;
一、函数模板
1、函数模板的引入
首先,根据我们前面所学的函数重载,我们可以实现不同版本的同一功能的函数,比如,我们要实现一个相加的函数,如下;
int Add(int x, int y)
{
return x + y;
}
double Add(double x, double y)
{
return x + y;
}
int main()
{
int x1 = 3, x2 = 5;
double d1 = 3.3, d2 = 5.5;
cout << Add(x1, x2) << endl;
cout << Add(d1, d2) << endl;
return 0;
}
我们通过这样书写两份功能一样的代码实现了两种不同类型相加函数的实现,可是,我们会觉得这种写法好麻烦,而且代码重复很多,那我们以后想实现另一种的类型相加,是不是又要再写上一份这样的代码呢?为了解决这样的痛点,C++推出了函数模板的概念;
2、函数模板的使用
我们在函数前添加 template <class T1, classT2 ......>,尖括号是我们的模板参数列表,我们可在其中添加我们想要数量的参数;其中 class 也可以用 typename 代替,遇到这种写法也不要感到奇怪,作者个人比较喜欢用 class;根据语法我们实现了一份Add函数的函数模板;
template <class T>
T Add(const T& x, const T& y)
{
return x + y;
}
而在实例化模板部分呢,我们可以有以下几种写法;
int main()
{
int x1 = 3, x2 = 5;
double d1 = 3.3, d2 = 5.5;
cout << Add(x1, x2) << endl;
cout << Add(d1, d2) << endl;
// cout << Add(x1, d2) << endl; //err 实例化时产生二义性,不知道应初始化为int还是double
cout << Add<int>(x1, d2) << endl; // 显示实例化为int
cout << Add<double>(x1, d2) << endl; // 显示实例化为double
return 0;
}
在实例化模板数据推导时,应该保证不会产生二义性,不然编译器也不知道应该初始化为哪种,实在不行可采用显式实例化,即在函数名加上想要实例化成的模板类型,这样实例化模板时就不会产生二义性(这时传参可能出现隐式类型转换);
3、函数模板的原理
实际上,函数模板也没有那么神秘,当你调用了一个函数模板时,实际上,表面看起来你是调用了函数模板,其实是在编译阶段,编译器用你的函数模板生成了你想要的那个函数,可以理解为编译器用你的函数模板,生成了一个对应的函数,并不是你的代码变少了,而是你让编译器去帮你干你原本要干的活;如果你怕编译器累着,也可以自己手动实现,hhhhhhh;
实际上也并不是什么神秘的技术,就单纯是拿你的函数模板在编译阶段实例化一个相应的函数;
4、模板函数的匹配原则
当出现了以下代码,会调用哪一个函数呢?
int Add(int x, int y)
{
return x + y;
}
template <class T>
T Add(T x, T y)
{
return x + y;
}
int main()
{
int x1 = 3, x2 = 5;
cout << Add(x1, x2) << endl; // 调用哪一个??
return 0;
}
编译器是允许模板函数与自己显示实例化的函数同时存在的,当他们同时存在时,编译器会调用现成的,而不会浪费资源再去实例化生成一个实例化函数;当然,如果你非要用函数模板生成的实例化函数,你可以在调用时实例化,也就是如下这种写法,但不推荐;
Add<int>(x1, y1); // 此时调用函数模板实例化的
二、类模板
类模板的使用也与函数模板类似,以下实现了一个栈的类模板;
template<class T>
class Stack
{
public:
Stack()
{
// ...
}
private:
T* _arr;
int _top;
int _capacity;
};
此时,类域内的所有对象都可以使用该模板参数;
类模板的实例化;
int main()
{
Stack<int> st1;
Stack<double> st2;
return 0;
}
注意:使用了类模板后,在实例化对象时,必须加上模板参数!可以把Stack<int> 当作一个整体的类型看待,在函数传参时,也需要加上模板参数!切记,当作一个整体类型;
// 切记不可把<int>丢掉!!切记!!
void func1(Stack<int>& st)
{
}
void func2(Stack<int> st)
{
}
int main()
{
Stack<int> st1;
func1(st1);
func2(st1);
return 0;
}