模板初阶
1、泛型编程
大家有没有发现啊,我们在实现一个函数的时候,形参的类型总是多种多样的,但是呢,不同的形参类型就只能对应不同的函数
举个例子:
我们实现swap这个函数,相比大家都比较熟悉了。
void swap(int& a,int& b)
{}
void swap(double& a,double& b)
{}
对于double和int我们就得实现两个swap函数,如果有更多的形参类型,就意味着我们要实现更多的swap函数.而这些swap函数的区别也仅仅在于形参参数的不同,实现方法都是一样的。
因此C++提出了C语言所不具有的 模板这个概念
那么模板究竟是什么呢?让我们来一探究竟吧
一、函数模板
我们先记住这个语法
template <typename T>
template 是一个关键字,就是模板的意思,T是模板参数————类似于函数参数。名字不固定,可以是T,可以是K等等。
而模板参数可以有一个,也可以有多个。多个模板参数就用逗号分隔开。
template <typename T1,typename T2>
而 typename 是类型名 也可以用class代替
让我们来动手体验一下.
namespace spj
{
template <class T>
void swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
}
int main()
{
int a = 10;
int b = 20;
spj::swap(a, b);
cout << a << " " << b << endl;
double c = 1.1;
double d = 2.2;
spj::swap(c, d);
cout << c << " " << d << endl;
return 0;
}
有没有发现我们通过一个swap函数达到了 既能交换int类型的数据,又能交换double类型的数据,其实不只是这两种类型,其他类型也是使用的。
那么函数模板实现的原理是什么呢?
我们可以简单理解为 在传参的时候, T对传进来的参数进行了自动识别。
举个例子:
我们使用int类型传参,那么T就能够识别到int,然后将自己转换为int。
注意事项:
1、进行传参的时候,传参类型应当相同,否则编译不过.****(针对只有一个模板参数)
例:
template <class T>
void swap(T& a, T& b)
swap(1, 2.1);报错的原因是因为推演实例化错误
T不知道该推演为int 还是double类型。
修改方法可以是:
swap((double)1, 2.1)
也可以是:swap(1, (int)2.1)。
也就是转换为同一个类型就可以了。
还有一种方法是显示实例化,也就是直接告诉模板参数我传的是什么
swap<int>(1,2.1)
2、如果要使用不同类型的参数进行传参
template <typename T1,typename T2>
T1?T2? Add(T1& a,T2& b)
{
return a + b;
}
如果涉及到返回值的话,该使用T1还是T2呢?还是都可以呢?
我们拿T1进行举例
template <typename T1,typename T2>
T1 Add(T1& a,T2& b)
{
return a + b;
}
Add(10,20.1);
答案是 30.返回值类型是T1,在这里T1识别到的是int类型.因此会返回int类型。
在返回的时候进行强转。
Add(10.1,20);
答案是30.1.
因此 返回值的类型 是根据 模板参数所推理出来的类型。
同理:
对于刚刚实现的swap函数有异曲同工之处。
template <typename T1,typename T2>
void swap(T1& a,T2& b)
{
T1 temp = a; //这里使用T1 还是T2呢?
a = b;
b = temp;
}
答案是,使用T1或T2 会得到不同的结果。
我们来画图解释一下
同理 如果temp的类型是T2,用同样的方法进行推演。
二、类模板
与函数模板相似,在类前 加入关键字 template
template <typename T>
class Stack
{
public:
Stack() :_a(nullptr), _top(0), _capacity(0)
{
if (_capacity == 0)
{
_a = new T[4];
_capacity = 4;
}
}
private:
T* _a;
int _top;
int _capacity;
};
为什么要有类模板呢?
举个例子:如果我们要建一个char类型和int类型是堆.
我们就要实现两个类
class
{
private:
int* _a;
int capacity;
int top;
};
class
{
private:
char* _a;
int capacity;
int top;
};
如果我们使用类模板的话,我们就可以避免这样的情况发生。这也就是跟函数模板相同的原因,我们采用了泛型编程的思想。
但使用方法与函数模板上有区别。
我们在实例化对象的时候,需要将模板参数给实例化
例:
stack s1;这样是不行的。因为模板参数是推理不了参数类型的。
正确使用:
stack<int> s1. 需要将类型进行实例化。
在后续接触到STL的时候,类模板会经常使用到.后面再继续更新