STL与泛型编程

   C++提供了二种功能强大的抽象方法:面向对象编程与泛型编程。面向对象编程大家一定很熟悉了,这里就不再哆嗦了。提到泛型编程(Generic Programming),有的人可能还不太熟悉,但是提到STL,你就一定会有所耳闻了。 
    generic Programming的思想精髓是基于接口编程(相对于OOP,连多态所需的基类都不要了),它的技术出发点是选择子,核心技术是:类型推导、类型萃取、特化/偏特化,其成果是STL库:一组通用容器和一组操作于通用容器上的通用算法。STL(Standard Template Library,标准模板库) 其实就是泛型编程的实现品,STL是由Alexander Stepanov(STL之父)、David R Musser和Meng Lee三位大师共同发展,于1994年被纳入C++标准程序库。STL虽然加入C++标准库的时间相对较晚,但它却是C++标准程序库中最具革命性的部分,同时也是C++标准程序库中最重要的组成部分。由于新的C++标准库中几乎每一样东西都是由模板(Template)构成的,当然,STL也不会例外。所以,在这里有必要先概要说明一下模板的有关概念。 

模板概念 
    通过使用模板可以使程序具有更好的代码重用性。记住,模板是对源代码进行重用,而不是通过继承和组合重用对象代码,当用户使用模板时,参数由编译器来替换。模板由类模板和函数模板二部分组成,以所处理的数据类型的说明作为参数的类就叫类模板,而以所处理的数据类型的说明作为参数的函数叫做函数模板。模板参数可以由类型参数或非类型参数组成,类型参数可用class和typename关键字来指明,二者的意义相同,都表示后面的参数名代表一个潜在的内置或用户定义的类型,非类型参数由一个普通参数声明构成。 
    说明一个问题最好的方法就是举例,stl的使用暂且不说(这个开发环境带有参考手册),先来看看一个函数(算法)或容器(数据结构)是怎么模板化的,原理清楚了使用应该就不是问题。 
    依据简单原则,我们来看一个最简单的算法: “线性查找”,此算法用来从一含有一堆未排序的数据的容器中查找一个值,如果找到,返回下标,否则,返回一个表示没找到的值。 

    首先, 我们看最简单的用例: 从整行数组中查找值, 约定失败返回 -1, 因为正常下标都是从0开始的整数。 

    int find(const int a[], int n, int key) 
    { 
        for(int i=0; i< n; i++) 
        { 
           if(a[i] == key) 
               return i; 
        } 
        return -1; 
    } 

    这个算法大概是最简单最常用的了, 现在, 我们希望它不仅可以对int对象,还可以对double对象,对用户定义的任何对象使用,安以前的常规做法,就需要对每个类型编写一个同样的算法。嗯,当然,你可以 Ctrl+C, Ctrl+V来完成^^, 不过,现在我们用模板,有更好的方法了: 
   template<typename T> 
    int find(const T a[], int n, T key) 
    { 
        for(int i=0; i< n; i++) 
        { 
           if(a[i] == key) 
               return i; 
        } 
        return -1; 
    } 

     // 调用: 
     int a[5] ={ 1, 2, 3, 5, 6}; 
     int i = find(a, 5, 2); // i==1 
    现在,我们可以对任意的类型T进行查找,要求是 T 类型可以用" == " 来比较, 这对大部分来说是没问题的,但是对C风格字符串来说就不对了,因为我们希望比较的是字符串的内容,而不是地址, 好在可以为特别的类型定义特别的实现(这称之为“模板偏特化”): 

     template< > 
     int find< char* > (const char* a[], int n, char* key) 
     { 
        for(int i=0; i< n; i++) 
        { 
           if(strcmp(a[i], key)==0) 
               return i; 
        } 
        return -1; 
    } 
     
    好了, 现在可对字符串操作了, 但是, 我如果想要不分大小写比较呢? 或者,是其它的对象,我需要多种不同的比较呢? 我们把测试条件也模板出来! 

template<typename Type, typename Equaler> 
int find(const Type* array, int n, Type key) 
   int i = 0; 
   for(int i = 0; i < n; i++ ) 
   { 
       if(Equaler()(key, array[i])) 
       { 
           return i; 
       } 
    } 
   return -1; 

现在,对于比较部分, 全部用第二个模板参数来确定, Equaller 的定义可以如下 
//等于比较器 
template<typename Ty> 
struct equal 
    bool operator()(const Ty& _left, const Ty& _right) 
   { 
      return (_left == _right); 
   } 
}; 

分大小写的比较器可以如下: 
template< > 
struct equal<char* > 
   bool operator()(const char* & _left, const char* & _right) 
   { 
     return (strcmp(_left, _right) == 0 ); 
   } 
}; 

不分大小写的比较器可以如下: 

struct equalNoCase 
   bool operator()(const char* & _left, const char* & _right) 
   { 
      return (stricmp(_left, _right) == 0 ); 
   } 
}; 

//不分大小写的查找可以这样调用, 分大小写的 用 euqal<char*> 
int i = find< char*, equalNoCase<char* >  >(strs, n, strKey); 

函数也可以换一种写法,  这样就可以直接传递参数给函数, 不需要用尖括号指定模板了 
template<typename Type, typename Equaler> 
int find(const Type* array, int n, Type key,Equaler eql ) 
   int i = 0; 
   for(int i = 0; i < n; i++ ) 
   { 
       if( eql(key, array[i]) ) 
       { 
           return i; 
       } 
    } 
   return -1; 

// 调用 
int i = find(strs, n, strKey,equalNoCase() ); 

好了,现在我们看看, 加入要查找符合某个条件的呢, 可以用一个一元操作符来测试 
template< typename T > 
struct Pr 
    bool operator()(const T& v) 
    { 
        if(...)  //满足测试条件 
          return true; 
        else 
          return false; 
    } 
}; 

现在可以写一个 find_if 
template<typename Type, typename Pr> 
int find_if(const Type* array, int n, Type key,Pr  pr ) 
   int i = 0; 
   for(int i = 0; i < n; i++ ) 
   { 
       if( pr(array[i]) ) 
       { 
           return i; 
       } 
    } 
   return -1; 

    现在,我们看看,我们的find函数可以工作得很好了,不过, 如果要找小于5的你得写一个pr, 小于6的你得又写pr, 是不是很烦呢? 现在,我们实现一个绑定器,绑定我们要的参数,以便可以这样使用: 
    int find_if(array, n, key,bind2nd(less<int>,5) ); //小于5的条件 

// 下面实现一个简单的绑定器 
为了实现绑定,我们需要修改下 less比较器, 加上类型信息 
template<typename T> 
  typdef T arg_type;// 增加这个类型定义 
  // 
}; 

template < typename Fn_type,  Fn_type::arg_type rgiht> 
struct bind2nd 
    typedef  Fn_type::arg_type arg_tye; 

    bind2nd(Fn & fn,  arg_type rgiht): _Func(fn), _Value( right) 
    bool operator()( const arg_tye & left) 
    { 
         return fn(left, right); 
    } 
    Fn_type   _Func;// 二参数的函数对象 
    arg_type  _Value;// 右边的操作数 
}; 

    同样你给greater, equal 添加一个 typedef 定义。 好了,现在你可以用这个绑定器绑定大于, 等于, 小于的比较器了。 

    stl 的实现为了更通用, 比这个要复杂一些, 但是原理是一样的。 

    现在我们的 find_if、find 函数可以作用在任何 可以通过下标访问的数组上了, 但是, 如果使用的是不能通过下标的容器呢? 比如 list 等,下次我们将把位置指示器模板化, 这个就是 iterator 了, 通常翻译的是 迭代器, 其实叫位置指示器更确切,其行为类似于指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值