文章目录
前言:泛型编程,大家应该都听过,c++是支持泛型编程的,C语言不支持,泛型编程的实现离不开模板。模板由函数模板,类模板组成。
1.函数模板
函数模板代表了一个函数家族,它可以被实例化成具体的函数,但必须是使用时被参数化,根据参数而实例成具体的函数。
比如交换函数,我可能要交换的数据是整型,浮点型,字符型等等,要是C语言我们就需要写对应的版本,但是c++支持泛型编程,我们来用一个函数模板就能在使用时实例化成对应的交换函数版本。
(1) 函数模板的格式
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表)
{}
(2) 交换函数的函数模板
template<typename T>
void Swap( T& x, T& y)
{
T temp = x;
x = y;
y = temp;
}
(3) 通过调试 交换函数 ,理解函数模板原理
#include<iostream>
using namespace std;
template<typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
//整型
int a = 1; int b = 2;
Swap(a, b);
//浮点型
float x = 1.1; float y = 2.2;
Swap(x, y);
//字符型
char i = 'a'; char u = 'b';
Swap(i, u);
return 0;
}
首先,Swap(a,b),整型的交换。
进入Swap函数后,可以看到left,right的类型是整型,进行整型交换,
再来看Swap(x,y),交换浮点型数据,
最后交换字符型数据,
分析:可以看到,在进行传参时,根据传参的不同,函数也会实例化成相应的函数,具体可看上面调试中left,right的变化,传int型时,left,right为int;传float时,left和right为float;传char时,left和right为char。
具体关系如下图:
也就是说,函数模板可以根据传参来自动推演成相应的函数。
当然,如果不想让函数模板去推演,也可以自己显示的实例化,
Swap<类型>(a,b);
(4) 模板参数的匹配机制
上文中,Swap函数只能匹配同类型的数据交换,如果交换int和char就会出错,
如下:
//混合型交换
int m = 10; char w='Q';
Swap(m, w);
原因很简单,函数模板中只有一个参数T,这表明传来的参数必须是相同的类型,否则就会实例化失败。
解决方案:模板中再给一个参数T1,两个不同参数,所以可以有俩种类型。
template<typename T,typename T1>
void Swap(T& left, T1& right)
{
T temp = left;
left = right;
right = temp;
}
那么可以想到,如果传的参数是三种类型,那么必须有三个模板参数去对应。
还有一种情况,假如Swap函数有专门处理整型类型数据转换的,会不会去调用模板去实例化呢?还是说根本就不会实例化,直接去调用专门的函数呢?
我们可以实验一下:
void Swap(int& x, int& y)
{
int temp = x;
x = y;
y = temp;
}
调试来看一看:
小黄标指在专门的函数上,说明它去调用专门的对应函数了。
结论:
对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板(这种情况说明那个函数还不够专门)。
2. 类模板
类模板就是,c++中的类中成员需要支持泛型,也就是类的成员可能根据需求类型会发生改变。
(1) 类模板的格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
举一个例子:类模板A可以创建包含不同类型的数组,数组包含_a个元素。
template<class T>
class A
{
public:
A(int a = 1)
{
_a = a;
ptr = new T[_a];
}
~A()
{
delete[] ptr;
ptr = nullptr;
_a = 0;
}
private:
int _a;
T* ptr;
};
可以看到,这个类模板A就实现了这样的功能,可以根据需求,包含不同类型的数组,具体类型不是靠传参,不能像函数模板那样去推演,只能显示的实例化类模板。
(2) 类模板的使用
#include<iostream>
using namespace std;
template<class T>
class A
{
public:
A(int a = 1)
{
_a = a;
ptr = new T[_a];
}
~A()
{
delete[] ptr;
ptr = nullptr;
_a = 0;
}
private:
int _a;
T* ptr;
};
int main()
{
A<int> m(3);
A<double>w(4);
A<char>E(5);
return 0;
}
模板类和普通的类不同:
- 普通的类:类名+对象名就可以了,类名就是类。
- 模板类:类名<类型> +对象名才行,并且类名<类型>才是类。
通过调试带大家看一看。
可以清楚看到,int*,double*,char*,根据需求开辟了不同类型的数组。
还需要强调一点:类模板中函数放在类外进行定义时,需要加模板参数列表。
template <class T>
A<T>::~A()
{
delete[] ptr;
ptr = nullptr;
_a = 0;
}
结尾语:以上是模板的基础应用,对于高级用法后面会更新的。