C++模板函数 & 模板类

思考:为何要引入模板呢?ios

对于这个问题,咱们经过一个例子让你们切实体会一下,模板给咱们带来的好处。程序员

当咱们要写一个比较大小的函数时,若是咱们要比较的两个数是整数,那么,咱们每每会这样来定义:数组

首先,传入两个int类型的参数a和b,而后去比较a和b的大小,将较大的数经过return返回出来,使它成为max这个函数的返回值。函数

但是,若是咱们又想比较两个float类型的数,那么这两个数也要取其中的最大值,那么,咱们能够写成这样:学习

一样的,若是咱们要比较两个char类型的字符呢,那么,咱们就能够写成这样:spa

经过上面的三个函数,你们能够看到,除了数据类型有所不一样以外,它们的运算逻辑是彻底相同的。那么,在这种状况下,你们会发现,若是咱们要写上这三个函数,对于咱们程序员来讲,简直太痛苦,并且所作的工做重复性较高,没有多大意义。因此,这种状况下,最好有一种方案:将类型做为参数传递进去,经过计算机帮咱们把这三个函数作出来,作出来以后就能够分别经过这三个函数来处理int类型,float类型、char类型的数据,而且可以根据传入的数据类型作相应的处理,从而获得相应的返回值,因而,做为程序员的咱们就省事多了。指针

那么,若是想要这么作,咱们须要学习三个关键字:template(模板)、typename、class注意:这里的class不是表示类的,而是表示数据类型的)。咱们来看一看具体的使用方法。code

咱们看到,当咱们要定义一个函数模板的时候,咱们须要经过关键字template来声明一个函数模板,经过class或者typename这个关键字来声明一个参数T,这个参数T可以代表一种数据类型。若是咱们再去写max这个函数的时候,它的返回值就写上T,它的参数类型也用T来做参数类型,而且函数内部逻辑不变。因而,当咱们将来要传入的是int类型的时候,计算机就会经过这个函数模板将它实例化一个模板函数,这个时候模板函数中的数据类型T就变成了int,就会去处理int类型的数据了。若是遇到的是float类型,T就变成了float,而后也就会去处理float类型的数据了。咱们来看一看具体的使用。对象

当咱们用max去比较两个数的大小的时候,若是咱们不指定它的数据类型T,那么,计算机会根据本身的判断去选择一种模板函数,选择以后就会用本身的计算逻辑,好比说这里的第一行代码,传入的100和99都是int类型的,它就会自动实例化一个int类型的模板函数,而后对100和99进行相应处理,并将处理互殴的结果100返回出来做为返回值赋值给ival。若是咱们制定了数据类型,好比说这里的第二行代码,一样是调用max函数,可是这里用了一对尖括号指明了数据类型为char类型,那么,这就指定了所传入的参数必定要是char类型的参数才能够,而后将返回值返回出去。blog

那么,咱们上面所说的模板函数和函数模板,咱们给你们指出来,以下所示:

函数模板是函数的模具,经过模子就能够生产出一个一个的函数,那么,经过函数模板生产出来的函数就称之为模板函数。在计算机当中,若是咱们仅仅写出了函数模板而没有去使用它,那么,计算机是不会产生任何代码数据的,由于它也不知道要产生什么样的代码数据。只有当咱们去使用函数模板的时候,计算机才会知道具体要实例化出一个怎样的模板函数来,这个时候才会产生真正的代码数据,从而才会参与逻辑运算

下面咱们来看一看经过关键字typename如何来定义一个函数模板。

以上是一个数据交换的函数,在使用上没有什么不一样,咱们能够看到:当咱们调用swap函数的时候,咱们所传入的参数是int类型,这就意味着前面的数据类型T就替换成了int。

变量做为模板参数

变量做为模板参数如何来使用呢??咱们直接来看使用的方法:

这里经过关键字template声明了一个函数模板,注意,这个时候传入的再也不是类型,而是一个变量,而这个变量在咱们真正去使用的时候才会将这个函数模板实例化成一个模板函数,它才是一个肯定的值,若是不使用它,仍然是没有任何代码数据产生。使用的时候,咱们在这传入的不是类型,而是一个肯定的数值,此时这个值就是一个常数,只不过在这体现出来的看上去像是一个变量,但真正编译出来就是一个常数。

多参数函数模板

模板有的时候变得很复杂,由于咱们不能肯定,在咱们的平常应用当中只有一个类型做为模板的参数,若是有多个参数咱们该如何去处理呢?咱们来看一看:

咱们看到,当有多个参数时,须要用逗号(,)隔开,而且,尖括号中的两个typename关键字是不能省略的。当咱们写成这样以后,T和C就变成了函数模板的参数,那么,在display函数中,其中一个参数a是T类型的参数,另外一个参数b是C类型的参数。使用的时候,咱们须要将T类型和C类型都指定出来,以下:

在这咱们调用display的时候,就爱那个int类型做为第一个参数,将string类型做为第二个参数

注意:typename和class这两个关键字能够混用,它们所起的做用是同样的。以下所示:

咱们还能够这样来混用,以下所示:

在这里,用typename哎定义了一个数据类型T,另一个则是int类型的变量size。咱们在使用的时候,就能够制定出size的值以及T的数据类型,以下:

这里调用display的时候,第一个传入的参数是int类型,即用int代替了上面的T,第二参数是5,即用5代替了上面的size,打印的效果就是打印出5个a。





函数模板与重载

其实函数的模板看上去就已经具备重载的意思了,由于经过函数的模板能够拓展出无数个模板函数来,咱们能够尽情的去想象它可以拓展出来的数据类型,那么,这些拓展出来的模板函数之间就能造成了一种重载关系。此外,不一样的函数模板所拓展出来的模板函数也可以造成重载。咱们来看一看:

上面这三个函数的模板骑士都有所不一样。第一个函数模板只有一个参数a,第二个函数模板有两个参数a和b,那么第一个与第二个就造成了参数个数不一样的函数重载关系。第三个函数模板也只有一个参数,看上去与第一个函数模板同样,可是,第三个函数模板,它的模板参数自己就有两个:一个是T类型,一个是int类型的size变量,那么这个时候当咱们去使用的时候:

你会发现,经过三个不一样的函数模板能够实例化出三个不一样的模板函数,这三个不一样的模板函数之间就造成了重载关系。你们请注意:咱们在定义出函数模板的时候,函数模板自己并非互相重载的关系,由于当咱们仅仅定义出函数模板,在内存当中并不会产生任何的数据代码,只有当咱们去使用它的时候,编译器才会为咱们产生出相应的函数代码出来,这些函数代码之间才能够称得上具备重载关系。



类模板

为何会有类模板呢?这和函数模板的道理是同样的,是由于在不少使用场合下,一个类会用到不少次,而在用的时候发现不少重复的地方,只有它的数据类型不一样,因此这个时候咱们就要用到类模板。咱们看下面一个例子:

在这里,咱们定义了一个类:MyArray,其中,咱们用T这种数据类型来定义了它的数据成员的指针,而且还定义了一个成员函数display,请你们注意,这个成员函数的定义时写在类内的。那么在类模板的状况下,在类内定义成员函数的时候并无什么不一样。可是,在类模板的状况下,在类外定义成员函数的时候,则大不相同,以下:

咱们看到,在类模板的状况下,当在类外定义成员函数的时候,须要在这个成员函数的上方,先把template <class T>这一行代码写出来(注意,它的写法与在类的上方的写法是同样的),咱们每定义一个成员函数,都要在这个定义的成员函数上方加上这一行代码;同时,咱们须要在这个成员函数的类名后面用尖括号括上相应的参数T(若是有两个参数,须要用逗号隔开)。在使用的时候,咱们若是实例化一个对象,咱们就须要在类名的后面用尖括号括上当前这个对象是什么数据类型。

与函数模板同样,类模板并不产生实质性代码,只有当咱们去实例化一个对象时,将类的后面写上一个固定的参数,这个时候才会产生数据代码,而这套数据代码,咱们就称之为模板类,那么,这样的关系与前面所讲的函数模板与模板函数的道理同样。

下面咱们来看一看,类模板当中使用多个参数的状况。当咱们有多个参数时,咱们举了一种比较复杂的状况(既有类型做为参数,也有变量做为参数)以下:

使用的时候,也分为类内定义和类外定义的成员函数。对于类内定义,咱们没必要多说了,而对于类外定义,以下:

在使用的时候,咱们一样也要给定两个参数,以下:


须要你们特别注意的是若是咱们要定义一个类模板,咱们必须将类的声明以及类的定义部分写在同一个.h文件当中,将来在使用的时候把它包含进来,因此在MyArray.cpp文件当中没有写任何代码,而是将全部代码都写在了MyArray.h文件当中了。

另外,定义类模板的时候,还须要注意如下内容

  • 在类的上面一行,要写上template关键字,而后加上模板参数列表;
  • 若是咱们在类的内部定义函数时(类内定义),那么,咱们不须要有什么特别须要注意的地方;
  • 若是咱们在类的外部定义函数时(类外定义),则须要在每个函数的上面加上template关键字,而后再加上模板参数列表。另外,在函数定义时,还须要用尖括号括上相应参数。


#ifndef TEMPLATE_H
#define TEMPLATE_H



template <typename T, int size ,int value>//类模板
class Template
{
public:
    Template();
    ~Template();

    void display();
private:
    T * m_pArr;//类模板里的参数
};

template <typename T, int size ,int value>//模板类的每一个成员函数都要以模板的形式呈现
Template<T,size,value>::Template(){//模板类的每一个成员函数都要以模板的形式呈现

    m_pArr = new T[size];//建立类型为T的size个数组,赋值给指针

    for(int i = 0; i < size; i ++){
        m_pArr[i] = value;//全部赋值为value
    }
}

template <typename T, int size ,int value>
Template<T,size,value>::~Template(){//模板类的每一个成员函数都要以模板的形式呈现


    delete []m_pArr;
    m_pArr = NULL;
}



template <typename T, int size ,int value>
void Template<T,size,value>::display(){//模板类的每一个成员函数都要以模板的形式呈现

    for(int i = 0;i < size; i++){
        cout<<"value :"<<m_pArr[i]<<endl;
    }
}


#endif // TEMPLATE_H
#include <iostream>
#include "template.h"
using namespace std;



int main()
{

    Template<int,10,5> tmp;
    tmp.display();

    getchar();
    return 0;
}