c++模板

来源:互联网 发布:知其所以然技术论坛 编辑:程序博客网 时间:2024/05/22 05:23

1.模版是实现代码的一种工具,可以实现参数类型化,就是把参数定义为类型,从而实现代码的可重用性。

 

模版分为类模版和函数模版。模版就是把功能相似,仅数据类型不同的函数和类设计为通用的函数模版和类模版,提供给用户。

模版是泛型编程基础。所谓泛型编程就是独立于任何特定类型的方式编写代码。

简单说:类是对象的抽象,模版是类的抽象,用模版定义具体类。

 

函数模版的语法:

Template <class /typename T1class /typename T2 >

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

函数定义体

例:

Template  <typename  T,typename T2>  T max1(T a,  T2 b)

{ return a > b?a:b;}

 

类模版:

Template <class T>

class 类名

//类定义

};

类模板中,成员函数不能被声明为虚函数。类模版不能和另外一个实体的名称相同。

 

Template <class T>

Class pair{

Public:

pair(T first , T second)

    { value1 = first; value2 = second;   }

Private:

T value1, value2;

};

 

定义一个对象存放整数24101

pair  <int>  myobject (24, 101);

 

 

 

 

 

 

定义一个成员函数getmax获取最大值:

Template <class T>

T pair:: getmax()

{

return value1>value2?value1:value2;

}

 

为了交换不同类型的变量的值,我们通过函数重载定义了四个名字相同、参数列表不同的函数,如下所示:

void Swap(int *a, int *b)                     //交换int 变量的值

{ int temp = *a; *a = *b; *b = temp; }

void Swap(float *a, float *b)              //交换 float 变量的值

{ float temp = *a; *a = *b; *b = temp; }

void Swap(char *a, char *b)             //交换 char 变量的值

{ char  temp = *a; *a = *b; *b = temp; }

void Swap(bool *a, bool *b)             //交换 bool 变量的值

{ char temp = *a; *a = *b; *b = temp; }

 

 

Template  <typename T>   void Swap(T *a, T *b)

{

      T  temp = *a;

      *a = *b;

      *b = temp;

}

 

template是定义函数模板的关键字,它后面紧跟尖括号<>,尖括号包围的是类型参数(也可以说是虚拟的类型,或者说是类型占位符)。

typename是另外一个关键字,用来声明具体的类型参数,这里的类型参数就是T

template<typename T>被称为模板头。

 

 

2.所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。

这个通用函数就称为函数模板(Function Template)。

 

函数模版的用法:

template < typename 类型参数1 , typename 类型参数2 , ...> 

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

     {
    //在函数体中可以使用类型参数
}

typename关键字也可以使用class关键字替代,它们没有任何区别。

 

函数模板也可以提前声明,不过声明时需要带上模板头,并且模板头和函数定义(声明)是一个不可分割的整体,它们可以换行,但中间不能有分号。

 

 

3.类模板

声明类模板的语法为:

   template<typename 类型参数1 , typename类型参数2 ,>

   class 类名

   {
    //TODO:
};

 

类模板和函数模板都是以 template 开头(当然也可以使用 class,目前来讲它们没有任何区别),后跟类型参数;

 

类型参数不能为空,多个类型参数用逗号隔开。

 

类外定义成员函数时仍然需要带上模板头,格式为:

Template  <typename 类型参数1 , typename类型参数2 ,>
返回值类型 类名<类型参数1 ,类型参数2, ...>::函数名(形参列表)

    {
    //TODO:
}

 

   第一行是模板头,第二行是函数头,它们可以合并到一行,

不过为了让代码格式更加清晰,一般是将它们分成两行。

 

4.使用类模板创建对象时,需要指明具体的数据类型:

Point<int, int>   p1(10, 20);

Point<int, float>  p2(10, 15.5);

Point<float, char*> p3(12.4, "东京180");

 

除了对象变量,我们也可以使用对象指针的方式来实例化:

Point<float, float>     *p1 = new Point<float, float>

(10.6, 109.3);

Point<char*, char*>   *p = new Point<char*, char*>

("东京180", "北纬210");

 

需要注意的是,赋值号两边都要指明具体的数据类型,且要保持一致。下面的写法是错误的:

//赋值号两边的数据类型不一致

Point<float, float> *p = new Point<float, int>(10.6, 109);

//赋值号右边没有指明数据类型

Point<float, float> *p = new Point(10.6, 109);

 

5.函数模板的重载

我们定义了 Swap() 函数用来交换两个变量的值:

一种方案是使用指针, 另外一种方案是使用引用,这两种方案都可以交换 intfloatcharbool等基本类型变量的值;

   但是却不能交换两个数组。

 

//方案①:使用指针:

 template<typename T>   void Swap(T *a, T *b)

{ T temp = *a; *a = *b; *b = temp; }

 

//方案②:使用引用

template<class T>

void Swap(T &a, T &b)

{ T temp = a; a = b; b = temp; }

 

 

   

     交换两个数组唯一的办法就是逐个交换所有的数组元素,

     template<typename T> void Swap(T a[ ], T b[ ], int len)

    {

         T temp;

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

          { temp = a[i]; a[i] = b[i]; b[i] = temp; }

     }

 

在该函数模板中,最后一个参数的类型为具体类型(int),而不是泛型。

并不是所有的模板参数都必须被泛型化。

 

6. 类模版

    在使用类模板创建对象时,需要显式的指明实参(也就是具体的类型)。 例如对于下面的 Point 类:

template<typename T1, typename T2> class Point;

 

在栈上创建对象: Point<int, int>   p1(10, 20);

在堆上创建对象: Point<char*, char*>  *p =

     new Point<char*, char*>(“东京180度”,“北纬210度”);

 

 

 

 

7.函数模板

     对于函数模板,调用函数时可以不显式地指明实参

(也就是具体的类型)。请看下面的例子:

template<typename T> void Swap(T &a, T &b); //函数声明

Int n1 = 100,   n2 = 200;         Swap(n1, n2);

float f1 = 12.5, f2 = 56.93;         Swap(f1, f2);

 

虽然没有显式地指明 T 的具体类型,但是编译器会根据n1 n2f1f2的类型自动推断出 T的类型。

     

     这种通过函数实参来确定模板实参(也就是类型参数的具体类型)的过程称为模板实参推断。

 

    对于普通函数(非模板函数),发生函数调用时会对实参的类型进行适当的转换,以适应形参的类型。

    这些转换包括:

1.算数转换:例如 int转换为 floatchar转换为 int

         double 转换为 int等。

2.派生类向基类的转换:也就是向上转型,

3.const 转换:也即将非 const类型转换为 const类型,

例如将 char * 转换为const char *

4.数组或函数指针转换:如果函数形参不是引用类型,那么数组名会转换为数组指针,函数名也会转换为函数指针。

5.用户自定的类型转换。

两个函数原型:

void func1(int n, float f);

void func2(int *arr, const char *str);

 

它们具体的调用形式为:

int nums[5];        

char *url = "http://c.biancheng.net";

func1(12.5, 45);

func2(nums, url);

1.对于 func1()12.5会从double转换为int45会从int转换为float

2.对于 func2()nums会从int [5]转换为int *url会从char *转换为const char *

 

 

原创粉丝点击