模板类,函数类(参数决定类型)

来源:互联网 发布:mac怎么切换大写字母 编辑:程序博客网 时间:2024/06/05 21:25
 

模板类

目录

简介
  1. 优势
函数模板
类模板
展开

编辑本段简介

  模板是根据参数类型生成函数和类的机制(有时称为“参数决定类型”),通过使用模板,可以只设计一个类来处理多种类型的数据,而不必为每一种类型分别创建类。

优势

  创建一个类型安全函数来返回两个参数中较小的一个,如果不使用Templates,必须要编写一系列如下的函数:
  // min for ints
  int min( int a, int b )
  return ( a < b ) ? a : b;
  // min for longs
  long min( long a, long b )
  return ( a < b ) ? a : b;
  // min for chars
  char min( char a, char b )
  return ( a < b ) ? a : b;
  //etc...
  使用templates,可以减少重复部分,形成一个函数:
  template <class type> type min( type a, type b )
  return ( a < b ) ? a : b;
  模板能够减少源代码量并提高代码的机动性而不会降低类型安全。
  何时使用模板
  模板经常被用来实现如下功能:
  >> 创建一个类型安全的集合类(例如,堆栈)用来处理各种类型的数据
  >> 为函数添加额外的类型检查以避免获得空指针
  >> 合并操作符重载组来修改类型行为(例如智能指针smart pointer)
  大多数以上应用可以不用模板实现;但是,模板具有以下几个优势:
  >> 开发容易。你可以只为你的类或函数创建一个普通的版本代替手工创建特殊情况处理。
  >> 理解容易。模板为抽象类型信息提供了一个直截了当的方法。
  >> 类型安全。模板使用的类型在编译时是明确的,编译器可以在发生错误之前进行类型检查。

编辑本段函数模板

  函数模板(function templates)
  使用函数模板,你可以指定一组基于相同代码但是处理不同类型或类的函数,例如:
  template <class type> void MySwap( type& a, type& b )
  {
  type c( a );
  a = b; b = c;
  }
  这段代码定义了一个函数家族来交换函数的参数值。从这个template你可以产生一系列函数,不仅可以交换整型、长整型,而且可以交换用户定义类型,如果类的构造函数和赋值操作符被适当地定义,MySwap函数甚至可以交换类。
  另外,函数模板可以阻止你交换不同类型的对象,因为编译器在编译时知道参数a和b的类型。
  你可以像调用一个普通函数一样调用一个函数模板函数;不需要特殊的语法。例如:
  int i, j;
  char k;
  MySwap( i, j ); //OK
  MySwap( i, k ); //Error, different types.
  可以对函数模板的template参数作外部说明,例如:
  template<class T> void f(T) {...}
  void g(char j)
  { f<int>(j);
  //generate the specialization f(int)
  }
  当template参数在外部说明时,普通固定的类型转换会转换函数的参数为相应的函数模板参数。在上面的的例子中,编译器会将(char j)转换成整型

编辑本段类模板

  类模板(class templates)可以使用类模板创建对一个类型进行操作的类家族。
  template <class T, int i> class TempClass
  {public:
  TempClass( void );
  ~TempClass( void );
  int MemberSet( T a, int b );
  private:
  T Tarray;
  int arraysize;
  };
  在这个例子中,模板类使用了两个参数,一个类型T和一个整数i,T参数可以传递一个类型,包括结构和类,i参数必须传第一个整数,因为I在编译时是一个常数,你可以使用一个标准数组声明来定义一个长度为i的成员数组模板与宏的比较(Templates vs. Macros)在很多方面,模板类似预处理宏,用给定的类型代替模板的变量。然而,模板和宏有很大的区别:
  宏:
  #define min(i, j) (((i) < (j)) ? (i) : (j))
  模板:
  template<class T> T min (T i, T j) { return ((i < j) ? i : j) }
  使用宏会带来如下问题:
  >> 编译器没有办法检查宏的参数的类型是否一致。宏的定义中缺少特定类型的检查。
  >> 参数i和j被被调用了2次。例如,如果任一个参数有增量,增量会被加两次。
  >> 因为宏被预处理程序编译,编译器错误信息会指向编译处的宏,而不是宏定义本身。而且,在编译阶段宏会在编译表中显露出来。
  模板和空指针的比较(Templates VS. Void Pointers)
  现在很多用空指针实现的函数可以用模板来实现。空指针经常被用来允许函数处理未知类型的数据。当使用空指针时,编译器不能区分类型,所以不能处理类型检查或类型行为如使用该类型的操作符、操作符重载或构造和析构。
  使用模板,你可以创建处理特定类型的数据的函数和类。类型在模板定义里看起来是抽象的。但是,在编译时间编译器为每一个指定的类型创建了这个函数的一个单独版本。这使得编译器可以使用类和函数如同他们使用的是指定的类型。模板也可以使代码更简洁,因为你不必为符合类型如结构类型创建特殊的程序。
  模板和集合类(Templates and Collection Classes)
  模板是实现集合类的一个好方法。第四版及更高版本的Microsoft Foundation Class Library使用模板实现了六个集合类:CArray, CMap, CList, CTypedPtrArray, CtypedPtrList和 CtypedPtrMap。
  MyStack集合类是一个简单的堆栈的实现。这里有两个模板参数,T和i,指定堆栈中的元素类型和堆栈中项数的最大值。push 和 pop成员函数添加和删除堆栈中的项,并在堆栈底部增加。
  template <class T, int i> class MyStack
  {
  T StackBuffer;
  int cItems;public: void MyStack( void ) : cItems( i ) {}; void push( const T item ); T pop( void );}; template <class T, int i> void MyStack< T, i >::push( const T item ){ if( cItems > 0 ) StackBuffer[--cItems] = item; else throw "Stack overflow error."; return;} template <class T, int i> T MyStack< T, i >::pop( void ){ if( cItems < i ) return StackBuffer[cItems++] else throw "Stack underflow error.";}
  模板和智能指针(Templates and Smart Pointers)
  C++允许你创建“智能指针”(“smart pointer”)类囊括指针和重载指针操作符来为指针操作增加新的功能。模板允许你创建普通包装来囊括几乎所有类型的指针。
  如下的代码概括了一个简单的计数垃圾收集者参考。模板类Ptr<T>为任何从RefCount继承的类实现了一个垃圾收集指针。
  #include <stdio.h> #define TRACE printf class RefCount { int crefs;public: RefCount(void) { crefs = 0; } ~RefCount() { TRACE("goodbye(%d)\n", crefs); } void upcount(void) { ++crefs; TRACE("up to %d\n", crefs);} void downcount(void) { if (--crefs == 0) { delete this; } else TRACE("downto %d\n", crefs); }}; class Sample : public RefCount {public: void doSomething(void) { TRACE("Did something\n");}}; template <class T> class Ptr { T* p;public: Ptr(T* p_) : p(p_) { p->upcount(); } ~Ptr(void) { p->downcount(); } operator T*(void) { return p; } T& operator*(void) { return *p; } T* operator->(void) { return p; } Ptr& operator=(Ptr<T> &p_) {return operator=((T *) p_);} Ptr& operator=(T* p_) { p->downcount(); p = p_; p->upcount(); return *this; }}; int main() { Ptr<Sample> p = new Sample; // sample #1 Ptr<Sample> p2 = new Sample; // sample #2 p = p2; // #1 will have 0 crefs, so it is destroyed; // #2 will have 2 crefs. p->doSomething(); return 0; // As p2 and p go out of scope, their destructors call // downcount. The cref variable of #2 goes to 0, so #2 is // destroyed} 类RefCount 和 Ptr<T>共同为任何一个从RefCount继承的能够提供整数的类的每一个实例提供了一个简单的垃圾收集解决方案。注意使用一个参数类如Ptr<T>代替多个一般类如Ptr的主要好处在于这种形式是完全的类型安全的。前面的代码保证Ptr<T>可以被用在几乎任何T* 使用的地方;相反,一个普通类Ptr只能提供到void*固有的转换。
  例如,考虑一个用来创建和处理文件垃圾收集的类,符号、字符串等等。根据类模板Ptr<T>,编译器可以创建模板类Ptr<File>,Ptr<Symbol>, Ptr<String>等等,和它们的成员函数:Ptr<File>::~Ptr(), Ptr<File>::operator File*(), Ptr<String>::~Ptr(), Ptr<String>::operator String*()等等。
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 移门衣柜门与柜体有冶缝隙怎么办 推拉门关门时撞门框声音大怎么办 两岁宝宝夏天不盖被子怎么办 家里有好多会爬的小黑虫怎么办 刚贴的壁纸怎么发霉了怎么办 晚上睡觉一熄灯有许多小虫子怎么办 一岁宝宝夏天爱哭不爱吃饭怎么办 合肥房子卖了户口没地方迁怎么办 忌作灶的日子新房装橱柜了怎么办 刮水泥浆的墙面刮不住腻子怎么办 小学二年级孩子偷钱 老师怎么办 发现自己读初中的儿子偷钱怎么办? 做错事了得不到亲人的原谅怎么办? 窗口 窗套与墙缝隙大怎么办 中班小孩还不会认1到10怎么办 母猫奶头被小猫咬伤了怎么办 口红不小心弄到衣服上怎么办 脖子后背疼的睡不着觉应该怎么办 君子兰用高锰酸钾泡浓度高了怎么办 五个月宝宝认人不要奶奶睡怎么办 幼儿小班安全卡鱼刺了怎么办教案 学籍在一年级学生在二年级怎么办 一岁多宝宝挑食不爱吃饭菜怎么办饭 换了新手机微信好友显示不全怎么办 朋友人在外地 联系不上怎么办 幼儿连字母都记不住该怎么办 车一边轮子掉农村路边小水沟怎么办 20个月小孩脾气急燥怎么办 给小孩上户口母亲是外国人怎么办 宝宝在学校不敢跟老师说话怎么办? 早教课上宝宝总爱乱跑怎么办? 20天大的宝宝4天不拉大便怎么办 还不会说话的小孩子脾气大怎么办 两岁的宝宝说话变的口吃怎么办 37周b超宝宝腿短怎么办 3岁的宝宝说话说不好怎么办 小儿说话晚的原因宝宝说话晚怎么办 22岁的儿子在家不说话怎么办 儿子2周4个月了不说话怎么办 三岁的宝宝还不会数数怎么办 2岁宝宝晚上不睡觉不听话怎么办