目录
泛型编程
模板
函数模板
函数模板的实例化
隐式实例化
显示实例化
类模板
我们知道STL(标准模板库)是C++学习的精华所在,在学习STL之前我们得先学习一个新的知识点-------模板。那么模板究竟是什么呢?围绕着这个问题,进入我们本期的内容。
泛型编程
在学习模板之前我们得先了解什么是泛型编程?
大家可以先想想生活中的一个场景,比如说临近过年了,我们一般在大年夜要吃饺子,要吃饺子,饺子皮必然是必不可少的,比如要包100个饺子,一般情况下我们就得擀上100个饺子皮,但是这样是很费时间的,有没有什么好得方法呢?有人就想到了用一个圆形的模具,使用了圆形的模具之后,生成饺子皮的效率就高了很多了。
当然,上面的场景只是生活中的场景,我们来看看C++中的场景,比如说现在我们要进行两个数的交换,我们一般会写一个交换函数。假设这两个数的类型为int,char,double。相关的代码如下:
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void Swap(char& a, char& b)
{
char tmp = a;
a = b;
b = tmp;
}
void Swap(double& a, double& b)
{
double tmp = a;
a = b;
b = tmp;
}
我们发现上述三个交换函数就只有类型不同,其它的都是相同的,如果要交换的变量的类型很多时,难道我们也要写这么多的函数吗?有没有一种方法可以改变解决这种情景呢?在C语言中这种情景确实没有很好的解决办法,因为C语言是不支持泛型编程的,但是C++是支持泛型编程的,我们提出了模板这一概念,可以很好的解决这一情景带来的问题。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。
模板
模板分为函数模板和类模板。
注意:在函数中我们将函数括号中的参数称作形参,在函数模板这里我们将尖括号中的称作模板参数。
函数模板
函数模板:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定 类型版本。
注意:在函数中我们将函数括号中的参数称作形参,在函数模板这里我们将尖括号中的称作模板参数。
所以对于上面交换三种类型变量的函数我们可以用函数模板实现,代码如下:
template<class T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10, b = 20;
double c = 10.1, d = 9.3;
char ch1 = 'a', ch2 = 'b';
Swap(a, b);
Swap(c, d);
Swap(ch1, ch2);
return 0;
}
运行截图:
通过函数模板,不难发现,不同类型的变量确实发生了交换。
但是大家思考一个问题。这三次交换,调用的是同一个函数吗?我们通过底层代码为大家展现:
很明显,这三次调用不是同一函数,因为函数参数列表的参数类型不同,函数的地址也是不同的。这其中到底发生了什么?
其实,这里发生了函数模板的实例化。我们将实参传给形参之后,编译器会根据我们实参的类型生成对应的函数模板,这相比之前我们手动实现函数,提高了效率。
函数模板的实例化
所谓函数模板的实例化,就是再调用函数模板时,编译器通过传递的实参的类型推导对应类型的函数,然后将实参传递给所推导的函数的形参。这就是函数模板的实例化。
隐式实例化
隐式实例化:就是我们在调用函数模板时,编译器会根据我们传递的实参的类型自动推导函数模板中的模板参数类型。
注意:隐式实例化时,我们不能让传递的实参为两种类型,因为函数模板中只有一个模板参数,当模板参数只有一个时,一次函数调用只能实例化一种类型,这是如果传递了两种类型,就会报错。
对于两个数求和的代码:
template<class T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
int a = 10, b = 20;
double c = 10.1, d = 9.3;
Add(a, d);
return 0;
}
错误示例如下:
我们用了int,double两个类型的实参去调用函数模板,这是不行的。
要解决这种错误有两种方法,一种是类型强制类型转换,一种是显示实例化,告诉编译器模板参数类型。
显示实例化
显示实例化:在函数名后的<>中指明模板参数类型。
在进行函数模板的调用时,我们直接告诉编译器模板参数的类型。具体代码如下:
template<class T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
int a = 10, b = 20;
double c = 10.1, d = 9.3;
Add(a, d);
Add<int>(a, d); //告诉编译器,模板参数的类型
return 0;
}
类模板
我们为什么要引入类模板呢?
在我们学习栈时,我们学习栈一般是数组栈,栈的每个元素是有具体的类型的,但是如果我们想让栈的元素的类型灵活的变化,按照之前C语言的方式实现栈的话,我们可能得实现多个只有元素类型不同的栈(每次都用typedef定义不同的数据结构),会导致代码冗余,当然这只是针对相对空间较小栈,如果我们后期学习了一个相对空间很大的数据结构,如果再次碰到了这种情景,如果还是这样进行实现,就会导致大量资源的浪费,所以针对这种情况,C++的大佬们发明了类模板。我们依然以栈这个数据结构作为讲解,栈的类模板代码如下:
template<class T>
class Stack
{
public:
Stack(int capacity=4)
:_top(0)
, _capacity(capacity)
{
_a = new T[capacity];
}
~Stack()
{
delete[] _a;
_a = nullptr;
_capacity = _top = 0;
}
private:
T* _a;
int _Top;
int _capacity;
};
int main()
{
Stack<int> st1;
Stack<double> st2;
Stack<int*> st3;
return 0;
}
注意:在之前我们类类型就是类名,但是在有了类模板之后,类类型就不再是类名了,此时也不能进行类模板的隐式实例化,只能进行类模板的显示实例化,所以此时的类类型就是类名<模板参数类型> 。此时我们创建对象不能再次使用类名创建对象,必须使用类名<模板参数类型> 创建对象
以上便是我们本期模板的所有内容,模板是学习STL的基础,小伙伴们一定要掌握模板的相关内容。
本期内容到此结束^_^