前言:本教程使用到的工具是vs2010;
目录
为什么要使用模板?
template模板
函数模板
类的模板
template模板的本质
总结
我们先来大概了解一下模板的概念,下面是菜鸟教程对于模板给出的解释:
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector
知道了模板的基本概念之后,我们先来看一段代码:
#include
#include void Sort(int* arr, int nLength)
{int ii;int kk;for(ii=0;iiarr[kk+1]){int temp = arr[kk];arr[kk] = arr[kk+1];arr[kk+1] = temp;}}}
}int main()
{return 0;
}
这是一段最简单的冒泡排序代码,看不懂没关系,知道怎么用就行;
继续往下:
我们定义一个整型数组,里面存放被打乱了的几个整数,如下:
然后我们调用函数,如下:
调试:
此时顺序是乱的,单步步过F10一下:
已经重新排序;
好了,我们现在知道这个函数怎么用了,继续往下看:
我们知道在C语言中'a'和'b'是可以比较的,因为字符类型其实也就是整型,他们的比较是ascii码值的比较,'a'的ascii码值是97,'b'的是98,'c'的99....以此类推,那么我们是不是可以对字符进行排序呢?
可以;
我们复制一下上面的整形冒泡排序代码,改一点就行了,如下:
我们定义一个字符数组,并进行排序,如下:
调试:
单步F10:
排序成功!
虽然我们达到了目的,但是我们违背了面向对象编程设计思想: 提高代码的复用性,减少重复代码的编写;
但是,template模板可以帮我们解决这个问题,这也就是我们为什么要使用模板的原因了;
template模板又分为函数模板和类模板,我们先讲函数模板;
函数模板的格式:
template
返回值类型 函数名(参数列表)
{
函数体;
}
下面我们给我们的冒泡排序函数加上模板:
!!!!注意看图片上的文字!!!!;
然后把我们想要自适应类型的地方全部换成T就行了,如下:
下面我们来测试,先测试int类型,如下:
调试:
F10:
没有问题,接着我们测试char类型:
调试:
F10:
没有问题;
那么这个函数能对自己定义的类型进行排序吗?
当然可以;
如下,我们定义一个类:
我们首先要知道,如果我们想给我们自己定义的类的对象进行排序的话,那么我们肯定要进行运算符重载;
我们观察一下刚刚冒泡排序的函数,需要比较类的对象大小的地方有哪些:
我们可以发现,就这一个地方需要对我们类的对象进行比较大小,而且是大于号;
那么我们只需要重载'>'即可,如下:
下面我们进行测试,定义对象,并调用模板函数:
调试:
F10:
ok,没有问题;
template模板成功的帮我们减少了重复代码的编写,提高了代码的复用性;
先看代码:
#include
#include template
class CBase
{
public:int x;int y;char a;char b;int MAX(){if(x>y) return x;if(xb) return b;if(a
定义了一个类,类中有四个成员变量;分别是int型的x、y;char型的a、b;
然后x和y比较谁大返回谁,a和b比较谁小返回谁;
我们来分析一下,这个类中大概有几个需要自适应类型的地方:
我们知道了这个类中大概有两个地方需要自适应类型,那么接下来先声明这个类为模板类:
因为我们有两个需要自适应的类型,所以这里的class参数有两个;
下面我们进行模板的替换:
替换好了,下面我们进行测试:
定义好对象以后,我们给对象的成员进行赋值:
我们调用比较的成员函数:
我们将鼠标悬停到MAX和MIN上:
可以看到,在我们CBase
观察r和t:
没有问题;
当然在类中的模板中,也是可以比较类的对象的,依旧需要重载运算符,这里我就不演示了;
下面我们来说一下,template模板的本质:
代码如下:
#include
#include class CBase
{
public:int x;int y;CBase(int x,int y){this->x = x;this->y = y;}bool operator>(CBase& right) // 因为我们'>'有两个操作数,左是this指针,那么这里的右操作数只能传一个参数;{return this->x > right.x && this->y > right.y; // 返回左操作数大于右操作数的结果,如果左大于右就为真,否则为假,达到了大于号的目的;}
};template // 只需要在这里加上模板声明即可
void Sort(T* arr, int nLength)
{int ii;int kk;for(ii=0;iiarr[kk+1]){T temp = arr[kk];arr[kk] = arr[kk+1];arr[kk+1] = temp;}}}
}int main()
{int arr1[5] = {2,1,4,5,3};char arr2[5] = {'c','b','d','a','e'};CBase c1(2,2),c2(1,1),c3(4,4),c4(3,3),c5(5,5);CBase arr3[5] = {c1,c2,c3,c4,c5};Sort(arr1,5); // 此处下断点system("pause");return 0;
}
首先我们先对arr1进行排序,断点下载Sort;编译、调试、alt+8转到反汇编,如下:
template模板在底层已经识别了改排序是int类型的排序,这没有问题;
然后我们将arr2也假如排序,那么template底层会怎么做呢?
编译、调试、alt+8反汇编:
可以看到template模板在底层又重新生成了一个函数,用于char类型的排序;
那么arr3如果加入排序的话,相比大家也都知道结果了,这里我就不测试了;感兴趣的话可以自己测试一下;
现在我们可以总结一下template的本质是什么了;
template模板并没有我们想的那么高大上,就是说仅仅一个函数可以千变万化,其实并不是千变万化也并不是一个函数;
我们刚刚也看到了,模板的底层就是通过看你传入参数的类型,给你分配一个适用你传入类型的函数,你传入int类型,他就给你一个适用int类型的函数,你传入char他就给你一个适用char类型的函数;如果你连续传入int、char等n个类型进行排序,那么他的底层就会给你分配n个函数,并不是一个函数实现的类型自适应;
1、我们使用模板的目的就是为了提高代码的复用性,减少重复代码的编写;
2、函数模板的底层并不是只有一个函数完成的,它是根据你传入参数的类型,给你分配一个适用你传入类型的函数;如果你连续传入n个类型,他就会给你分配n个函数;
结语:
文章讲义到此结束,如果有讲错的地方或者说讲的不好的地方,望指出;感谢大家观看!