前言
面向对象编程中的继承等功能,让我们能复用不少的代码,但实际编程中,还会有很多需要重复劳动的地方,比如我们要实现函数Swap,拥有交换的功能。
我们要交换两个整数,那就要写个:
void Swap(int&a,int &b)
{
int c=a;
a=b;
b=c;
}
如果我们还要能交换两个浮点数,那就要再重载一个:
void Swap(float&a,float&b)
{
float c=a;
a=b;
b=c;
}
如果我们还想要交换别的类型,就还需要再重载一遍,每种类型都要重载一次,但是每次函数的结构都差不多,非常麻烦。
所以C++中提供了模板与泛型的功能,同样的功能,你写一个模板之后,所有的类型就都可以适用。
函数模板
利用C++中的模板与泛型,我们可以这样写:
//也可以写成template <class T> 在C++98标准添加关键字typename前,都是用的class,C++98之后推荐使用typename关键字
template <typename T>//template关键字声明这是个模板,typename关键字后写入类型名的代称,可以指代任何类型,也就是说T是一种泛型
void Swap(T& a, T& b)//T类型的a,T类型的b
{
T c = a;
a = b;
b = c;
}
那么任何类型都可以进行交换的功能了。
模板函数写的其实是一类函数,并不是函数实例,在编译期,编译器会根据该模板函数会使用的类型,将其实例化编写成相应参数类型的函数实例。
重载模板函数
模板函数也可以被重载,如下:
template <typename T>
void Swap(T& a, T& b)//这个交换函数,只能交换单个元素
{
T c = a;
a = b;
b = c;
}
template <typename T>
void Swap(T&a[],T&b[],int n)//让我们重载个两个数组之间的交换函数
{
T c;
for(int i=0;i<n;++i)
{
c=a[i];
a=b[i];
b[i]=c;
}
}
模板的局限性
这里我觉得《C++ Primer Plus》中讲的不错,我直接截图过来吧。(不想重复造轮子了QAQ)
显式具体化
显式具体化,是为了处理某些泛型模板无法处理的特殊类型的时候,我们可以单独把这种特殊类型的函数写出来,单独处理。
比如我们新创建了个结构体表示学生,我只希望交换它们的姓名,其余的不交换我们就需要这样:
#include<iostream>
struct Student
{
std::string name;
float score;
};
template <typename T>//template关键字声明这是个模板,typename关键字后写入类型名的代称,可以指代任何类型,也就是说T是一种泛型
void Swap(T& a, T& b)//T类型的a,T类型的b
{
T c = a;
a = b;
b = c;
}
template <>//template这里不能填入泛型
void Swap<Student>(Student& a, Student& b)//显式定义Student的处理办法
{
std::string c = b.name;
b.name = a.name;
a.name = c.name;
}
int main()
{
Student a, b;
a.name = "A";
a.score = 1;
b.name = "B";
b.score = 0;
Swap<Student>(a, b);
std::cout << a.name << " " << a.score << std::endl;
std::cout << b.name << " " << b.score << std::endl;
}
输出结果:
B 1
A 0
隐式具体化
当你没有显式定义时,编译器会从重载的模板中寻找参数最匹配的来实例化函数,本篇教程不会详细展开编译器具体会以什么标准具体选择哪套函数来实例化,因为这部分内容对初学者来说较为深入,所以请自行百度…
类模板
直接看代码实现:
#include <iostream>
using namespace std;
template <typename T>//声明模板和泛型名称
class A
{
public:
//函数的参数列表使用模板类型
A(T t = 0)
{
this->t = t;
}
//成员函数返回值使用模板类型
T& getT()
{
return t;
}
private:
//成员变量使用模板类型
T t;
};
int main(void) {
//1.模板类定义类对象,必须显示指定类型
//2.模板种如果使用了构造函数,则遵守以前的类的构造函数的调用规则
A<int> a(211); // 构造函数仅在创建对象时使用 类名<参数类型> 对象名;
A<char> b('a');
cout << a.getT() << endl;
cout << b.getT() << endl;
return 0;
}
输出结果:
211
a
在上面的代码中,我们创建了两种类实例,一种是int类型的,一种是char类型的。
拓展内容
后记
其实C++中的模板和泛型,还有很多很多深奥的内容,但本系列教程是针对新手的,所以只讲了基本的内容,实际C++模板元编程还有变长参数、编译期实现switch分支、模板编程实现递归,模板元编程实现编译期堆排序等等,许多大佬拿模板元编程装逼,所以C++这个语言,写一辈子也不敢说精通,模板得占很大一部分原因。