C++ のテンプレートについての予備的な理解

 

目次

C++ のテンプレートについての予備的な理解

1 C++ 標準テンプレートでできること

2 テンプレートの分類

2.1 クラステンプレート

2.2 関数テンプレート

2.2.1 例

2.2.2 関数テンプレートの定義と使用方法

                                               C++のテンプレートについての予備的な理解

1 C++標準テンプレートでは何ができるのですか?

        プログラミングの過程で、「特定のアルゴリズムはデータ型と無関係であるが、プログラミングの際、さまざまなデータ型に適応させるために、それを解決するために複数のバージョンや複数のオーバーロードされた関数を作成する必要があることがよくあります。また、特定のデータ自体の論理構成は型に依存しないが、同じ異なるオブジェクトでも異なるバージョンを作成する必要がある」といった問題に遭遇することがあります。ですから、私たちはそのような夢を持っています。それは、標準のコンポーネント ウェアハウスを持ち、そこにあるすべてのコンポーネントが優れた、わかりやすい標準インターフェイスを持つことです。自分で繰り返しの作業をするのではなく、賢い人の知恵と経験をフルに活用することができれば、そのときの太陽はどれほど明るいことでしょう。
  ついにその日がやって来ました。C++ は私たちの夢の実現に役立ちます。

2  テンプレートの分類

2.1クラステンプレート

        場合によっては、次のステートメントがクラスを宣言しているように、機能が同じでデータ型だけが異なるクラスが 2 つ以上存在することがあります。

サンプルコード 1:

クラス Compare_int

{

公共:

    Compare_int(inta,int b)

    {

        x=a;

        y=b;

    }

    int max( )

    {

        return(x>y)?x:y;

    }

    int min( )

    {

        return(x<y)?x:y;

    }

プライベート:

    int x,y;

};

その機能は 2 つの整数を比較することであり、メンバー関数 max と min を呼び出すことで 2 つの整数のうち大きい方と小さい方を取得できます。2 つの浮動小数点数 (float 型) を比較する場合は、別のクラスを宣言する必要があります。

サンプルコード 2:

クラス Compare_float

{

公共:

    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