C++快速入门 (十五) 模板

本文详细介绍了C++中的模板和泛型编程,涵盖模板函数、模板类、重载、typename与class的区别、嵌套、非类型模板形参等内容,并进一步探讨了模板特化,包括函数和类模板的特化以及偏特化,为C++开发者提供了全面的模板使用指南。

一,模板和泛型

(1). 泛型编程
一般的函数只能对固定某一系列(基类及其派生类)类型进行操作。如

int  Add( const   int  &   t1,   const   int  &   t2)
{
      return   t1  +   t2  ;
}

该函数只能应用于 int 类型。显然不太利于重用(顺便提一句,面向对象的终极目标就是重用最大化)。如果写代码时可以使用某个占位符来替换实际的类型,编译时再确定,其重用性就大大提高。这种思想就叫做 泛型编程。C++中实现泛型的机制被称为模板。

(2). 模板函数
C++使用模板来实现泛型编程。 模板使用 关键字template 声明,放在要定义泛型的函数或类之前。

template <typename T>
T  Add( const   T  &   t1  ,   const   T  &   t2  )
{
      return   t1  +   t2  ;
}


其中   使用 关键字 typename 来定义占位符名称并放在一对尖括号中而泛型函数使用起来和普通函数一样。下边为一个完整示例

class   Base
{
public  :
      int  num;
      Base  operator+ (   const   Base  &   ba)   const
    {
          Base  tem;
        tem.num = num +   ba  .num;
          return  tem;
    }
};

template <typename T>
T Add(const T & t1 , const T & t2 )
{
      return   t1  +   t2  ;
}
int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
      Base  b1,b2;
    b1.num = 10;
    b2.num = 20;
      Base   b3 =  Add<Base>(b1,b2) ;
    cout << b3.num << endl;
}
//return: 30


模板 函数在调用时也可以不声明或只声明一部分(未声明类型需放在最右边)模板参数类型,因为编译器会根据实参类型自动推导获得。

Base  b3 =  Add(b1,b2) ;


(3). 模板函数重载
模板函数也支持重载。无论是模板类型参数不同 或者 函数签名不同(一般重载) 的同名函数 ,都视为重载。

template   <typename T>
T  Add( const   T  &   t1  ,   const   T  &   t2  ) {....}
template   <typename Ttypename C >
T  Add( const   T  &   t  ,   const   C  &   c  ) {....}

template  < typename   T>
T  Add( const T & t) {....}

int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
    Add(2,2);
    Add(2,2.6);
    Add(10);
}


(4). 模板类
同样也可以将一个普通类 通过模板 将其变为 泛型类。泛型类的声明方法和泛型函数相同。但 泛型类实例化时需显式指定模板参数类型

class   Base
{
public  :
      int  num;
      Base  operator+ (   const   Base  &   ba)   const
    {
          Base  tem;
        tem.num = num +   ba  .num;
          return  tem;
    }
};
template <typename T>
class   Example
{
public  :
      T obj;
      T Add(const T & t1 , const T & t2 ) const
    {
          return   t1  +   t2;
    }
};

int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
      Base  b1,b2;
    b1.num = 10;
    b2.num = 20;
      ExampleBase > ex;
    ex.obj = ex.Add(b1,b2);
    cout << ex.obj.num << endl;
}
//return: 30


(5). 模板类重载
模板类本身不支持重载(既不能通过模板类参数类型的不同进行重载定义),但模板类支持一种叫做模板特化的机制(后边会介绍)。  模板类的成员函数可以重载(和一般函数的重载条件相同)。

template  < typename   T
class   Dx
{
public  :
      void Say( T t ) {....}
    void Say( T t , int x) {....}
};



(6). 创建泛型类的实例
C++创建模板类的实例时,需要指定模板参数类型。如

template <typename T>
class  Cx
......
Cx <int > cx;
Cx <char> cx2;


这是因为编译器编译时需将 模板参数类型转换为指定的实际类型,才能进行编译规则检查并创建该类型。一定要明白: 同一个模板类不同模板参数类型所创建的是各自独立的类型。比如上边的两个Cx的实例为不同的类型。 


(7). typename 和 class 的区别
早期的C++是使用 关键字class 声明模板类型, 后来由于 class 容易让人产生混淆,就加入了 关键字typename,但为了保证兼容性用 class 声明模板类型的方式 也保留了下来,既   声明模板类型时 typename 和 class 两个关键字可以相互替换

template <class T class  Example 


但 typename 有另外一个功能:当模板内 使用模板类型的嵌套类型(嵌套类或 typeof 声明的别名等) ,为了避免歧义(比如声明方式和调用模板类型的静态函数一样)需 在其前边添加关键字typename ,告诉编译器按类型处理。

class   Base
{
public  :
      class   Pocket  // 嵌套类
    {
         ....
    };
};
template  < typename   T>
class   Example
{
public  :
      T  Add( const   T  &   t1  ,   const   T  &   t2  )
    {
          typename   T   :: Pocket   poc;     //  typename  表明该声明为一个类型声明,而非调用静态成员
        ....
         return   t1  +   t2;
    }
};

实际上对于现在的大多数编译器,不管你是否为类型添加  typename声明,都会正确的编译执行。 

(8). 嵌套
C++可以将一个模板类型作为另一个模板类或函数的泛型类型。

template  < typename   T>
class   Ax
{
};

template  < typename   T>
class   Bx
{
};
int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
      Ax< int> ax;
      Bx< Ax <int>> bx;
}


也可以在一个模板类中包含另一个模板类或模板函数(成员模板)

template <typename T>
class  Cx
{
public :
     template < typename Q >
     Q Say(  const  Qq1 )
    {
         return  q1 + 10;
    }
};

int  _tmain (  int  argc_TCHARargv [])
{
     Cx<  int> cx;
     short x = 200;
    cout << cx.Say(x) << endl;
}


(9). 非类型模板形参
C++(c98)中允许使用整型常量(包括整型字面值常量和整型常量表达式)或枚举(整型)作为模板类的模板参数类型 。如下示例

template  < int num>
class   Cx
{
public  :
      void  Say()
    {
        cout <<  num << endl;
    }
};
int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
     const int   num = 30;     // 需满足整型和常量两个条件。
      Cx< num> cx;
    cx.Say();
      Cx< 20> cx2;    // 整型常量。
    cx2.Say();
}



二,模板特化

(1). 模板特化
当 模板类或模板函数 需要为个别模板参数类型进行特殊照顾(代码不同)时,就需要用到模板特化。 特化以 关键字template 及其后跟一个空的尖括号开始。

(2). 模板函数特化
可以为模板函数定义模板特化。

template <typename T>
void Fun   (const Tt )
{
    cout <<  "非特化: " <<  t << endl;
}
template <>
void Fun <int> ( const intt )
{
    cout <<  "特化: " <<  t << endl;
}
int  _tmain (  int  argc_TCHARargv [])
{
     char x =  'a' ;
     int y = 20;
    Fun(x);
    Fun(y);
}


(3). 模板类特化
也可以为 模板类成员定义模板特化,但比函数特化多一步,就是 需要在特化类名后声明需特化的参数类型并放入尖括号括

template <typename T>
class   Ax
{
public  :
      T obj;
      void  Fun (   const T & )
    {
        cout <<   "非特化: "  <<   t  << endl;
    }
};
template <>
class   Ax   <int>   // 特化类型
{
public  :
      void  Fun   const int & )
    {
        cout <<   "特化: "  <<   t  << endl;
    }
};
int   _tmain  ( int   argc,   _TCHAR*   argv  [])
{
      char  x =   'a'  ;
      int  y = 20;
      Ax<char> a;
    a.Fun(x);    // 非特化
      Ax<int> a2;
    a2.Fun(y);   // 特化
}

在类的特化版本需要列出该类中所有需特化的成员,既   只有类的特化版本中列出的成员才支持特化类型。比如上例中,成员变量 obj 在特化版本实例中将无法访问。

(4). 偏特化
偏特化是相对于全特化来说的。像上边这种将所有模板参数类型都转换为模板特化类型的模板特化方式叫做全特化。而偏特化既只转换原模板的部分参数类型。如

template  < typename   T1,   typename   T2  >
class   Bx
{
    ....
};
template  < typename   T1>
class   Bx  < T1,   int>
{
    ....
};







-

<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>


作者:meiwm
出处: http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。

-


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值