C++泛型编程
STL(Standard Template Library)主要由两种组件组成:1)容器,包括vector、list、map等;2)操作上述容器的泛型算法,包括find()、sort()等。
泛型算法提供了许多可作用于容器类及数组类型上的操作。这些算法之所以被称为泛型,因它们与它们所要操作的元素类型无关。
泛型算法通过function template技术,达到与操作对象的类型相互独立;此外采用一对iterator(first以及last)来标识所要迭代的元素范围,即不直接操作容器本身,从而实现与容器无关。
本文以下内容通过引入查找算法,从解决问题的角度来剖析C++泛型编程思想,同时本文主要侧重于泛型编程思想、思维,但并不关注泛型算法本身。
1.查找算法
若此值存在于vector中,则返回一个指针指向该值;反之则返回0,以表示该值并不存在于vector中。
int* find(const vector<int> &vec, int value) { for(int i=0; i<vec.size(); ++i) if(vec[i] == value) return &vec[i]; return 0; }
2. 函数模板
若要此函数能处理任何类型,即与操作对象类型无关,则引入函数模板,以function template形式呈现。需要指出,该定义存在equality(相等)运算符。
template <typename elemType> elemType* find(const vector<elemType> &vec, const elemType &value) { for(int i=0; i<vec.size(); ++i) if(vec[i] == value) return &vec[i]; return 0; }
3.指针运算
若要该函数能处理vector与array内的任意类型元素,即与容器本身无关,需要指出,该元素类型的equality运算符皆已定义。很自然考虑到采用函数重载来实现,但本文不建议采用重载方式来实现。
本文采取化整为零的策略,将问题简单化的方式。由于vector与array为连续性容器,可以采用指针来指定容器起始位置,此外通过增加一个参数,用来表示容器的结束位置,从而使容器类型从参数类型中消失。以下存在两种解决方式,其本质内容是一致的。
1) 非标兵方式
template <typename elemType> elemType* find(const elemType *lt, int size, const elemType &value) { if(!lt || size <1) return 0; for(int i=0; i<size; ++i, ++lt) if(*lt == value) return lt; return 0; }
2) 标兵方式
template <typename elemType> elemType* find(const elemType *first, const elemType *last, const elemType &value) { if(!first || !last) return 0; for(; first!=last; ++first) if(*first == value) return first; return 0; }
4.迭代器Iterator
对于泛型编程,上述讨论忽略了非连续性容器,如list,但指针的算术运算并不适用于list。因此,为了真正做到与容器无关,我们引入迭代器Iterator。
template <typename IteratorType, typename elemType> IteratorType find(const IteratorType first, const IteratorType last, const elemType &value) { for(; first!=last; ++first) if(*first == value) return first; return last; }
find()的实现使用了底部元素类型的equality运算符,若底部元素所属类型并未提供该运算符,或者用户希望赋予该运算符不同意义,则上述find()的弹性就稍显不足了。解决方案存在两个:1)传入一个函数指针,取代原来固有的equality运算符;2)运用所谓的function object。
以下采用传入函数指针方式实现。
template <typename IteratorType, typename elemType> IteratorType find(const IteratorType first, const IteratorType last, const elemType &value, bool (*fun)(elemType, elemType)) { for(; first!=last; ++first) if(fun(value, *first)) return first; return last; }
5. 函数对象
函数对象(function object)是某种class的实例对象,这类class对函数调用运算符做了重载操作,如此则可使function object被当成一般函数来使用。
template <typename IteratorType, typename elemType, typename Fun> IteratorType find( IteratorType first, IteratorType last, elemType &value, Fun fun) { while((first=find_if(first, last, bind2nd(fun, value)))!=last) return first; return last; }
6.源码测试
为验证本文给出的C++泛型编程思想,限于时间因素,仅对函数对象部分进行验证。
测试源代码如下:
#include <iostream> #include <algorithm> #include <functional> using namespace std; template <typename IteratorType, typename elemType, typename Fun> IteratorType find( IteratorType first, IteratorType last, elemType &value, Fun fun) { while((first=find_if(first, last, bind2nd(fun, value)))!=last) return first; return last; } int main() { int ia[8]={1,1,2,3,5,8,13,21}; int data =0; for(int i =0; i<3; i++) { cout<<"input the data:"<<endl; cin>>data; int*p=find(ia, ia+8, data, equal_to<int>()); if(p==ia+8) cout<<"fail!"<<endl; else cout<<*p<<endl; } return 0; }
由于数组也是容器,因此本文采用数组进行测试也无可厚非。测试结果如下: