目录
1、函数模板
1.2 模板原理
2、多个模板参数
3、模板的显示实例化
4、模板的匹配
5、类模板
结语:
前言:
在C++中,模板分为函数模板和类模板,而模板的作用就是避免了重复的工作,把原本是程序员要做的重复工作交给了编译器去做。比如实现两个变量的交换函数,如果这两个变量是int类型则需要写一个int类型的交换函数,若是double类型则要再写一个double类型的交换函数,这样一来增加了很多反复的工作,若用函数模板就能解决以上问题,只需要写一个交换函数的模板,不管是什么类型,都可以通过该模板实现两个变量的交换。
1、函数模板
用交换函数举例子,如果要交换三种类型的数据则需要三个交换函数构造重载,写起来很冗杂而且可读性较差:
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
以上三个交换函数可以用一个函数模板代替,模板的格式如下:
template<class T1, class T2,......,class Tn>
//template是关键字,用于定义一个模板
//class替换成typyname也可以实现模板
//T1和T2是模板的关键部分,表示模板类型,可以接收任意的实参类型
//T1和T2也理解为模板参数
有了模板的格式,就可以写一个交换函数的模板,同时测试交换各种不同的数据类型,观察利用函数模板是否可以实现交换。测试代码如下:
#include<iostream>
using namespace std;
template<class T>//写在函数上面实现函数模板
void Swap(T& t1, T& t2)//Swap为函数模板名
{
T temp = t1;
t1 = t2;
t2 = temp;
}
int main()
{
int a = 2, b = 3;
double a1 = 2.2, b1 = 3.3;
char a2 = 'a', b2 = 'b';
Swap(a, b);
Swap(a1, b1);
Swap(a2, b2);
cout << a << " " << b << endl;
cout << a1 << " " << b1 << endl;
cout << a2 << " " << b2 << endl;
return 0;
}
运行结果:
这里要说明一点,模板的格式只对在他下面的第一个对象(即函数)有效,模板下面的第二个函数不是模板函数,比如:
1.2 模板原理
模板表面上减少了很多重复的工作,实际上是把原本由程序员进行的重复工作交给了编译器完成,在底层,编译器还是会根据不同的类型去生成该类型的交换函数。
2、多个模板参数
上述的模板参数只有一个,即:T。因为交换函数一般交换的两个变量类型都是相同的,因此一个模板参数就足够了,但是很多情况下函数要处理的数据类型不止一个,这时候该类情况的函数模板就需要多个模板参数了。
多个模板参数写法如下:
#include<iostream>
using namespace std;
template<class T1, class T2>//两个模板参数T1和T2
T1 func(T1& t1, T2& t2)//注意返回类型的选取
{
return t1 + t2;
}
int main()
{
int a = 2;
double b = 2.2;
cout << func(a, b) << endl;
}
运行结果:
如果模板涉及到返回类型,则要注意返回类型的选取。
3、模板的显示实例化
以上模板参数的具体类型都是自动的根据实参类型而定的,把这种自动识别类型的方式叫做隐式实例化,除了隐式实例化识别模板参数类型,还可以手动指定模板参数类型,即在调用模板函数时,在函数名后面加上'<>',并且在‘<>’中写上指定的类型。
如果只有一个模板参数是无法处理实参传递两个不同类型变量的情况,解决方法有两种:1、采用多个模板参数,2、使用显示实例化模板。
显示实例化示例代码如下:
#include<iostream>
using namespace std;
template <class T>
T Add(const T& t1,const T& t2)
{
return t1 + t2;
}
int main()
{
int a = 2, b = 3;
double a1 = 2.2, b1 = 3.3;
//b1是double类型,a是int类型,而模板参数只有一个T,不能够直接的将a、b作为实参传递
//因此可以手动指定模板参数T的类型为int,即显示实例化
cout << Add<int>(a, b1) << endl;
cout << Add<double>(a1, b) << endl;//double类型同理
return 0;
}
运行结果:
4、模板的匹配
如果存在一个函数模板和一个非模板且同名的函数,调用时实参类型若与非模板且同名的函数的类型对应上,则直接调用非模板且同名的函数。若调用时实参类型必须要经过函数模板才能生成该类型的函数,则调用函数模板。总而言之就是编译器会自动调用“当下最匹配”的函数。
示例代码如下:
#include<iostream>
using namespace std;
template <class T>
T Add(T t1, T t2)
{
return t1 + t2;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 2, b = 3;
Add(a, b);//此处调用int返回类型的Add
Add<int>(a, b);//若用显示调用则强制调用函数模板Add
double a1 = 2.2, b1 = 3.3;
Add(a1,b1);//只能通过调用模板函数Add来实现double类型的和
Add(2, 2.2);//一个int类型,一个double类型,显然一个模板参数不够,因此会调用int类型Add
return 0;
}
示意图:
5、类模板
之前通常都是用typedef进行类型的重命名,虽然该方法可以解决一部分问题,但是如果想让一个类实例化出两个不同类型的对象那么typedef就做不到了,只能通过显示实例化类模板,才能实现一个类可以实例化出两个不同类型的对象。
类模板的格式跟函数模板相似,只不过把函数替换成了类:
template<class T1, class T2,......,class Tn>
class A//A表示类模板名
{
}
示例代码如下:
#include<iostream>
using namespace std;
template<class T>
class Stack//Stack不再是具体的某一个类,而是一个类模板
{
public:
Stack(int capacity=4)//构造函数初始化
:_arr(new T[capacity])//可以申请不同类型的空间
,_top(0)
,_capacity(capacity)
{}
void push(const T& x)//压栈
{
_arr[_top++] = x;
}
T& operator[](int i)//显示栈里元素
{
return _arr[i];
}
private:
T* _arr;//_arr可以转变不同类型的指针
size_t _top;
size_t _capacity;
};
int main()
{
Stack<int> sti;//Stack不再是类型,Stack<int>才是类型。
Stack<double> std;//同理Stack<double>才是一个类型
//可以同时实现两个数据类型的Stack
sti.push(6);
std.push(6.6);
cout << sti[0] << endl;
cout << std[0] << endl;
return 0;
}
运行结果:
这里要注意的是:如果要通过类模板进行实例化对象,那么必须在类模板名的后面加上'<>',并且在‘<>’中表示类型,才能用类模板实例化对象。可以理解为类模板不是一个真正的类,而类模板+'<type>'才是一个真正的类。
结语:
以上就是关于C++模板的讲解,模板的出现减轻了程序员的工作,并且提供了代码的可读性和可维护性。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!