前言:
小编在前文讲述了C++的内存管理,下面我们来开始继续探索C++,开启C++又一个重要的内容,模版初阶的详解,代码时间到!
目录
1.泛型编程
1.1.引子
1.2.泛型编程
2.函数模版
2.1.函数模版的概念
2.2.函数模版的格式
2.3.函数模版的原理
2.4.函数模版的实例化
2.4.1.隐式实例化
2.4.2.显示实例化
2.5.函数模版的匹配原则
3.类模版
3.1.类模版的定义格式
3.2.类模版的实例化
4.总结
正文:
1.泛型编程
1.1.引子
在讲述泛型编程之前,小编先说一个例子来帮助各位读者朋友等会更好的去了解泛型编程,交换函数想必各位读者朋友都非常清楚,我们在C语言阶段就多次写过交换函数,小编也在二叉树那篇文章也写过交换函数,交换函数是帮我们去交换两个数,只不过这两个数的类型一般是一致的,就比如整形就得和整形进行交换,字符型就是要和字符型进行交换,就比如下面代码所示:
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;}void swap(char& a, char& b){char c = a;a = b;b = c;}
当我们在写一个项目的时候,那个项目既有整形的交换,浮点型的交换,字符型交换等等,这就意味着我们需要写三个重载的函数来完成交换函数,这样就体现不出来C++的简介美了,只会让我们增加写代码的量,而且还有代码的可维护性比较低,一步错步步错,这就得不偿失了,于是C++就推出了一个好用的功能,它就是模版,通过写模版我们就可以实现出交换函数,一个模版便可以解决上面三个函数的问题2,在具体讲解模版之前,小编先给各地读者朋友简单介绍一下泛型编程。
1.2.泛型编程
如果在C++中,也能存在一个摸具,通过给这个模具不断的添加材料(类型),来获得不同材料的铸件(即生成相同类型的代码),那就会节省很多开支,巧的就是前人已经将树栽好(模具已经建立),我们仅需再次乘凉即可。泛型编程就是编写与类型无关的通用代码,是代码复用的一种手段,我们今日所讲的模版,便就是泛型编程的基础,现在我们还不能完全去了解泛型编程,因为我们学的还没有那么深,现在我们仅需知道模版是泛型编程的基础即可,下面小编将会分两步进行模版的讲述,一个是函数的模版,一个是类的模版,下面,系好安全带,准备出发!
2.函数模版
2.1.函数模版的概念
函数模版其实代表了一个函数家族,该函数模版与类型无关,在使用被参数化,根据实参类型产生函数的特定;类型版本
2.2.函数模版的格式
下面小编就展示一下我们如何去写函数模版,如下所示:
template<typename T1, typename T2,......,typename Tn>返回值类型 函数名(参数列表){} //这里需要注意,此时上面的typename也可以写成class,怎么方便怎么来
以上便就是函数模版的写法,小编也在代码页写了,此时的typename也可以写成class,根据自己的习惯自己来就好,小编就喜欢使用class,可能现在很多读者朋友知道了用法,但是不知道如何实践,不要着急,下面小编就给各位读者朋友展示一下交换函数的模版应该怎么写。
template<class T>void swap(T& x, T& y){T m = x;x = y;y = m;}
上面便就是交换函数的模版,通过上面的书写,小编认为可以快速帮助读者朋友认识到函数模版的使用,下面小编给各位读者朋友讲述一下函数模版实现的原理。
2.3.函数模版的原理
函数模版其实和现实生活中的蓝图差不多,它本身并不是函数,是编译器用使用方式产生特定的具体类型的模具,所以其实模版就是帮助我们把本来我们自己要重复干的活交给了编译器来做,简单来说,我们通过模版让编译器变成了牛马,其中的过程如下图所示:
在编译器进行编译的阶段,对于函数模版的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。就比如:当用double类型使用函数模版的时候,编译器会对类型进行推演,进而确认T就是double,然后会产生一份专门处理double的代码,对于字符型,整形什么的也是这种情况,这就是函数模版的原理,简单来说,我们只要写好了模版,编译器会自动识别我们传递的类型从而把T转换成对应的类型,这个时候可能就有读者朋友会问了,对于上面小编所写的交换函数代码,倘若我填的第一个数是整形,第二个数是浮点型会有怎样的情况?不要着急,这涉及到了小编后面将会讲述的知识点,下面继续往后出发喽!
2.4.函数模版的实例化
用不同的类型使用函数模版的时候,称为函数模版的实例化,函数模版的实例化可以分为隐式实例化和显示实例化,现在我们先来进行前者的讲述。
2.4.1.隐式实例化
对于隐式实例化,说白了其实就是让编译器自己去识别此时的类型是什么,然后再把T转换成相应的类型即可,下面小编就用加法函数来给各位读者朋友举例,如下代码所示:
template<class T>T Add(T x, T y){return x + y;}int main(){int a = 12, b = 13;double c = 11.1, d = 12.2;int m = Add(a,b);Add(c, d);}
以上就是函数模版的隐式实例化,其实隐式实例化可以理解为我们什么也不需要做,让编译器帮我们去做就好了,不过此时的隐式实例化有一个情况倘若我们什么也不做是行不通的,就比如下面的实例化:
Add(a, d);
此时如果这么写的话编译器是给我们报错的,编译器报错的原因解释的不太行,这里小编提一嘴,报错后编译器给出的错误并不是完全正确的,有时候也是会有出错的情况,这里出错的原因其实就是此时的编译器是无法理解此时我们传的类型到底是什么,如果是按照第一个参数来断定T的类型,那么在第二个参数的时候便会出错,这让编译器摸不着头脑了,对于这个问题的解决策略,小编有两种解法可以帮助我们去把这个题目解决,如下所示:
1.把某一个元素强制类型转换(简单粗暴)
Add(a, (int)d);
2. 再写一个模版,不过此时的模版参数是两个,如下所示:
template<class T1,class T2>T1 Add(T1 x, T2 y){return x + y;}
对于第一种解决方案,小编认为还是蛮简单粗暴的,我们仅需随机在两个实参中把其中一个进行强制类型转换,从而让编译器不在犯迷糊,这是是用一个模版参量解决的方案之一,等会再显示类型转换的时候小编还会继续说,对于第二种方式,从严格意义上来讲,这是牵扯到了另一个模版了,如果我们在写题目的时候强制只有一个模版参量,那么就用第一个就可以了,反之使用第二个就好了,这便是隐式实例化的内容,下面开始讲解显示实例化。
2.4.2.显示实例化
对于显示实例化,它的用法也是很简单,我们仅需在函数名后加上<>即可,<>里面放置的是我们想要模版参数类型的实际类型,还是拿上面的整形和浮点型相加来进行举例,如果我们仅写一个模版参量的模版,那么强制类型转换是第一种解决方案,显示实例化则是第二种解决方案,如下所示:
Add<int>(a, d);
上面的代码就可以成功的去运行,因为此时我们使用了显示实例化,我们直接告诉了编译器,此时的模版参量就是int类型,编译器会在传参的过程中默默会生成隐式类型转换,把其中不是整形的转换为整形的,这就是显示类型实例化,难度也没有很大,各位读者朋友要知道这些实例化的方法,避免以后框框出错,下面我们进入函数模版最后一部分的讲解。
2.5.函数模版的匹配原则
肯定有读书朋友好奇,倘若我在写Add函数的时候,我写了一份整形类型的Add函数,写了一份单参量的函数模版,还写了一份双模版的函数模版,那么如果我调用Add(1,2),那么编译器会先去调用哪一份函数呢?不要着急,下面小编就介绍一下函数模版的匹配原则:
1.一个非模版函数可以和一个同名的函数模版同时存在,而且该函数模版还可以被实例化为这个非模版函数。这个说法是很容易让人去理解的,它意味着我们写完了一个模版Add以后,还可以写一个专属整型的Add函数,这俩是可以同时存在的,当这两个同时存在时,倘若我写了一个Add(1,2),编译器是会去调用整型的Add函数,而不会去使用模版,对于这个特点小编会在下面说的,这里我提前说一下,我们可以把模版想成菜单,把专属整型的Add函数认定为预制菜,编译器也不傻,有预制菜不用去用菜单自己炒一个,所以会去调用后面的函数,如下图所示:
int Add(int x, int y){return x = y;}template<class T>T Add(T x, T y){return x + y;}int main(){int a = 12, b = 13;Add(a, b);}
上图就显示了编译器是会调用现成的函数的,这就引出了第二个特点:
2.对于非模版函数和同名函数模版,如果其他条件都相同,在调用时会优先调用非模版函数而不会从该模版产生出一个实例,如果模版可以产生一个具有更好匹配的函数,那么还是会选择模版。这个特点小编也在前面说了,编译器又不是傻子,它虽然是一个牛马,但它知道如何做才会省事,所以它是会筛选模版函数的,就比如我写了两个模版参量的模版和一个模版参量的模版,如果我用了Add(1,2),那么是会去调用一个参量的,因为省事,但是如果我是Add(1,1.1),那么系统会直接调用两个模版参量的,如下所示:
template<class T>T Add(T x, T y){return x + y;}template<class T1,class T2>T1 Add(T1 x, T2 y){return x + y;}int main(){int a = 12, b = 13;double c = 11.1, d = 12.2;Add(a, b);Add(a, d);}
上面的调试过程便给我们展示了函数模版是如何去进行调用的,如果都是一个类型,编译器是会调用单模版参量的模版,因为省事,仅需把一个T转换为一个类型即可;如果有两个类型,那么自然的就会去调用两个模版参量的模版,这就是第二个匹配原则,下面来看第三个:
3.函数模拟不允许自动类型转换,但是普通函数可以进行自动类型转换。这个特点小编在上面也就展示出来了,这里就不多说明了,这就是函数模版的写法,下面我们继续出发,开始讲述类模版的写法~
3.类模版
3.1.类模版的定义格式
其实函数模版就已经讲述了许多模版的特性了,所以在类模版的时候小编会少说一点知识点,不然就显的我水字数了,下面小编就通过写代码的方式来帮助读者朋友知道类模版的定义格式:
template<class T1,class T2> //不一定就这些class wang{//类内成员函数的定义或者类内成员的定义};
上面就是类模版的定义格式,下面小编就以Stack类模版来帮助各位更好的去理解类模版:
template<class T>class Stack{public:Stack(int capcaity = 4){_arr = new Stack[capcaity];_size = 0;_capcaity = capcaity;}private:T* _arr;int _capcaity;int _size;};
上面就是小编简单写出来的Stack类模版,其实类模版和函数模版是很像的,只不过一个是函数一个类罢了,想必现在很多读者朋友会好奇类模版如何实例化,不要着急,下面小编就给各位读者朋友讲述一下类模版的实例化。
3.2.类模版的实例化
类模版的实例化和函数模版的实例化不同,类模版的实例化需要在类模版后面加上<>,然后将实例化对象的类型放在<>即可,简单来说类模版只可以显示实例化,因为如果不这样干的话编译器不知道如何去进行实例化,类模版的名字并不是真正的类,而实例化的结果才是真正的类,下面小编就给出类模版实例化的代码:
int main(){Stack<int>s1; //这样实例化可以额Stack s2; //这样实例化会报错,报错原因:缺少 类模板 "Stack" 的参数列表}
对于类模版的实例化,就必须显示实例化,这个点各位读者朋友一定要记住,以上就是类模版的讲解,讲解的还是蛮少的,因为函数模版其实就把模版的一些特性讲述的很多了,类模版的讲述仅仅就是帮助读者朋友去了解类模版的使用罢了。
4.总结
以上就是本篇博客小编所要讲述的内容,模版是C++比较重要的内容,也是一个为后面知识学习打下的基础,小编之后就要开始讲解STL库了,到时候将会给读者朋友们展示一下C++内容最爽的部分,如果本篇文章出现错误,请在评论区指出,小编定会及时回复,那么,大佬们,我们下一篇文章见啦!(牙疼真要命)