对C++ 的模板初步认识

 

目录

对C++的模板初步认识

1  C++ 标准模板能干什么

2  模板的分类

2.1 类模板

2.2 函数模板

2.2.1 举例

2.2.2 函数模板的定义与使

                                               C++ 的模板初步认识

1  C++ 标准模板能干什么

        在编程过程中,有时候我们会遇到类似这样的问题:某个算法它是跟数据类型无关的,但是在编程的时候,为了适应各种不同的数据类型,往往要写多个版本或多个重载函数来解决,另外又有一种情况,某个数据的逻辑组织方式,本身是跟类型无关的,但是同样不同的对象要写不同的版本。于是我们就产生这样的梦想:拥有一个标准的组件仓库,其中的组件都有着良好的、易于理解的标准接口。如果能够充分利用聪明人的智慧和经验,而不再自己去做大量重复工作,那时的阳光该是多么灿烂!
  这一天终于来到了。C++ 能帮我们美梦成真。

2  模板的分类

2.1 类模板

        有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类:

示例代码1:

class Compare_int

{

public:

    Compare_int(inta,int b)

    {

        x=a;

        y=b;

    }

    int max( )

    {

        return(x>y)?x:y;

    }

    int min( )

    {

        return(x<y)?x:y;

    }

private:

    int x,y;

};

其作用是对两个整数作比较,可以通过调用成员函数max和min得到两个整数中的大者和小者。如果想对两个浮点数(float型)作比较,需要另外声明一个类:

示例代码2:

class Compare_float

{

public:

    Compare_float(floata,float b)

    {

        x=a;

        y=b;

    }

    float max( )

    {

        return(x>y)?x:y;

    }

    float min( )

    {

        return(x<y)?x:y;

    }

private:

    float x,y;

};

 

        如上两个类,唯一不同的是数据类型不同,组织结构完全相同。类似于这样的问题在我们的编程中经常遇到,有人可能想到C++不是有函数重载么,利用函数重载可以避免重写两个类,但重复的代码量也是很多的。在C++中还有一种解决方式就是利用模板,利用模板类写出一个如下的类:

template<class numtype> //声明一个模板,虚拟类型名为numtype

class Compare  //类模板名为Compare

{

public:

    Compare( numtypea, numtype b)

    {

        x=a;

        y=b;

    }

    numtype max()

    {

        return (x>y)?x:y;

    }

    numtype min()

    {

        return (x<y)?x:y;

    }

private:

    numtype x,y;

};

从上述代码可以看出:

(1) 声明类模板时要增加一行:

        template <class 类型参数名>

(2) 原有的类型名int全都换成了虚拟类型参数名numtype。当建立类对象时,如将实际类型指定为int型,编译器就会用int取代所有的numtype,如果指定为float型,就用float取代所有的numtype。这样就实现一类多用。

由于类模板包含类型参数,因此又称为参数化的类。如果说类是对象的抽象,对象是类的实例,则类模板是类的抽象,类是类模板的实例。利用类模板可以建立含各种数据类型的类。

那么在声明一个类模板后,怎样使用它?怎样使它变成一个实际的类呢? 现在回顾一下类定义对象的方法:

Compare_int cmp1(4,7);//Compare_int是已声明的类

用类模板定义对象的方法与此相似,但是不能直接写成:

Compare cmp(4,7); // Compare是类模板名

具体的做法是:

Compare <int> cmp(4,7);

即在类模板名之后在尖括号内指定实际的类型名,在进行编译时,编译系统就用int取代类模板中的类型参数numtype,这样就把类模板具体化了,或者说实例化了。这时Compare<int>就相当于前面介绍的Compare_int类。

Compare是类模板名,而不是一个具体的类,类模板体中的类型numtype并不是一个实际的类型,只是一个虚拟的类型,无法用它去定义对象。必须用实际类型名去取代虚拟的类型。

        还有一个问题要说明:上面列出的类模板中的成员函数是在类模板内定义的。如果改为在类模板外定义,不能用一般定义类成员函数的形式:

numtype Compare::max( )

{

     return (x>y)?x:y;

}

而应当写成类模板的形式:

template<class numtype>

numtype Compare<numtype>::max( )

{

     return (x>y)?x:y;}

}

归纳以上的介绍,可以这样声明和使用类模板:

(1)  先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。

(2)  将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。

(3)  在类声明前面加入一行,格式为

    template<class 虚拟类型参数>

如:

    template<class numtype>   //注意本行末尾无分号

    class Compare                         

    {

              …//类体

    };   

(4)  用类模板定义对象时用以下形式:

        类模板名<实际类型名>  对象名;

        类模板名<实际类型名>  对象名(实参表列);

如:

    Compare<int>cmp;

    Compare<int>cmp(3,7);

(5)  如果在类模板外定义成员函数,应写成类模板形式:

    template<class 虚拟类型参数>

    函数类型 类模板名<虚拟类型参数>∷成员函数名(函数形参表列)

    {

      …

    }

 

说明:

(1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:

    template<classT1,class T2>

    class someclass

    {…};

      在定义对象时分别代入实际的类型名,如:

    someclass<int,double>obj;

(2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。

(3) 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。

 

2.2 函数模板

2.2.1 举例

        我们先来以一个求一个数的绝对值的例子开始。通常情况下一个数有各种不同的类型,所以在C++中可以使用函数重载的方法如下:

重载方法一:

#include<iostream.h>

#include<math.h>

int f(int i){return i>0? i: -i; }             //函数重载

long int f(long inti){  return i>0? i: -i; }  //函数重载

double f(float i){  return i>0? i: -i; }       //函数重载

 

void main()

{

    int a;  long int b;    float c;           //声明变量

    cin>>a>>b>>c;

    cout<<"整型:"<<f(a)<<" 长整型:"<<f(b)<<" 浮点型:"<<f(c)<<endl;

}

重载方法二:

#include<iostream.h>

#include<math.h>

class ABS     //求绝对值

{

public:

    int f(int i){ return abs(i); }                    //函数重载

    long int f(long int i){ return labs(i);}         //函数重载

    double f(float i){ return fabs(i); }              //函数重载

};

void main()

{  

    ABS n;                          //声明对象

    int a;  long int b;    float c; //声明变量

    cin>>a>>b>>c;

    cout<<"整型:"<<n.f(a)<<" 长整型:"<<n.f(b)<<" 浮点型:"<<n.f(c);

}

        如下我们使用函数模板来完成求一个数的绝对值

方法三:

template <typenameT> T abs(T a)

{

     returna>0?a:-a;

}

int main()

{

     int x=-1;

     inty=-0.99;

 

     cout<<"abs(x)="<<abs(x)<<endl<<"abs(y)="<<abs(y)<<endl;

     Sleep(5000);

 

     return 0;

}

结果为:

        

        第一次使用函数模板abs()时,实参为整数,由此可以推导出函数模板中的参数类型T为整型,函数的返回值也是整型。

        第二次调用abs()时实参为长整型,由此推导出函数模板中的参数类型T为长整型,函数的返回值为长整型。

 

2.2.2 函数模板的定义与使用

        利用函数模板,可以建立一个具有通用功能的函数,支持不同的函数参数和返回值,达到减少代码书写量的目的。

函数模板的定义形式如下:   

       template < class(typename)  T >

       返回值类型   函数名(参数表)

       {  函数体 }

其中:

        Template,class,typename为关键字;

        class或typename与T一起说明用户定义的数据类型,当函数不以类而存在时,使用关键字typename, 当函数要在某个类中声明时使用class;当然C++并没有规定必须得这样,而两个关键字都可以,这样做为了区分方便。

        类型形参T代表在函数模板中要使用的通用类型,在该函数的调用过程中,T被实参的类型具体化。

        函数模板实际上是定义了一类函数,对有些参数或数据成员的数据类型并不具体指定,而是作为模板的参数。等到使用模板时再根据实参的数据类型来确定模板参数的类型,得到模板的实例,称为模板的实例化。

例1:

template <typename T>

T abs(T value)

{    

    returnvalue>0? value: -value;  

}

若使用模板时:

abs(5);      //将模板参数的类型实例化为整型

abs(-3.8);    //将模板参数的类型实例化为实型

函数模板实例化后生成的函数称为模板函数。在程序中可用不同类型的实参来调用同一个函数,减少了代码的书写。

例2  编写选择排序的函数模板并实现排序

#include "stdafx.h"

#include <iostream>

#include <windows.h>

 

using namespace std;

//函数模板(两数交换)

template<typenameT> void Swap(T &x,T &y)

{

      T temp=x; x=y; y=temp; 

}

 

//函数模板(选择排序)

template<typenameT>  voidSort(T *v, int n)   //参数中有参数化类型的参数,也有普通类型的参数

{

    for(int i=0;i<n;i++)

     {

       for(int j=i;j<n;j++)

        {

          if(v[i]>v[j])

          {

               Swap(v[j],v[j+1]); //调用函数模板实例化后生成的模板函数

          }

        }

     }

}

 

//print函数模板(显示输出)

template<typenameT>  voidPrint(T *v,int n)

{

    for(int i=0;i<n;i++)

        cout<<""<<v[i];

    cout<<endl;

}

 

void main()

{

   shorta[10]={22,11,44,66,88,99,0,33,55,77};

   cout<<"排序前:";   

   Print(a,10);

   Sort(a,10);           //对整型数据排序

   cout<<"排序后:";   

   Print(a,10);

   char s[]="zcpkvrehaq";

   cout<<"排序前:";   

   Print(s,10);

   Sort(s,10);           //对字符型数据排序

   cout<<"排序后:";   

   Print(s,10);

   Sleep(5000);

}

运行结果为:

        

 

猜你喜欢

转载自blog.csdn.net/besidemyself/article/details/7201183