STL运用的C++技术(4)——重载函数

 STL是C++标准库的重要组成部分之一,它不仅是一个可复用的组件库,更是一个包含算法与数据结构的软件框架,同时也是C++泛型编程的很好例子。STL中运用了许多C++的高级技术。本文介绍重载函数。主要参考了《C++ Primer》和《STL源码剖析》。

       有人会问,STL大量运用了模板,哪里用过重载函数呢?首先介绍重载函数的概念。出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数(overloaded function)。这是《C++ Primer》上对于重载函数的定义。在STL中,提到重载函数,必须先介绍一下迭代器。两者貌似扯不上什么关系,慢慢道来。我们知道STL的迭代器有五个特性,分别是value_type、pointer、 reference、 difference_type以及iterator_category。其中value_type已在前面两篇文章中介绍,表示迭代器所指的数据类型。本文来介绍一下iterator_category。

       iterator_category表示迭代器的分类,共有五类。input_iterator、output_iterator、forward_iterator、bidirectional_iterator、random_access_iterator,分别是只读、只写、前向移动读写、双向移动读写、随机读写。任何一种迭代器,都属于其中的一种分类。比如单链表的迭代器属于forward_iterator,链表的迭代器属于  bidirectional_iterator,而双端队列的迭代器属于random_access_iterator。下面给出了代码示例,整理了一下。

[cpp]  view plain  copy
 print ?
  1. template <class T, class Ref, class Ptr>  
  2. struct Slist_iterator //单链表  
  3. {  
  4.     typedef T               value_type;      //迭代器所指数据类型  
  5.     typedef ptrdiff_t       difference_type; //两个迭代器间的距离  
  6.     typedef Ptr             pointer;         //迭代器所指的数据,不允许改变,即可以当右值  
  7.     typedef Ref             reference;       //迭代器所指的数据,允许改变,即可以当左值  
  8.     typedef forward_iterator_tag iterator_category; //前向移动  
  9.     ...  
  10. };  
  11. template<class T, class Ref, class Ptr>  
  12. struct List_iterator //双向链表  
  13. {  
  14.     typedef ptrdiff_t  difference_type;  
  15.     typedef T value_type;  
  16.     typedef Ptr pointer;  
  17.     typedef Ref reference;   
  18.     typedef bidirectional_iterator_tag iterator_category; //双向  
  19.     ...  
  20. };  
  21. template <class T, class Ref, class Ptr>  
  22. struct Deque_iterator  //双端队列  
  23. {   
  24.     typedef T  value_type;  
  25.     typedef ptrdiff_t difference_type;    
  26.     typedef Ptr pointer;  
  27.     typedef Ref reference;  
  28.     typedef random_access_iterator_tag iterator_category; //随机  
  29.     ...  
  30. };  

       说了这么多,与重载函数有什么关系呢?再给出一个函数就清楚了,这个函数就是advance函数,它用来移动迭代器。下面是该函数的定义,摘自HP的STL源码,做了修改。不难看出,外部的接口是advance函数,内部用了三个重载函数,这些函数的第三形参非常有意思,只是一个类型,没有变量名。这个形参仅仅s是用来激活重载,很神奇吧,强大的C++啊。

       这里进行函数的重载,应该是考虑到效率。对于双端队列,它的迭代器支持随机读写,那么调用第三个函数显然比调用第一个函数有效率。显然,这种效率的提升,与迭代器的移动能力息息相关。STL中的部分函数采用了这种策略,比如distance函数,为的是提高效率。

[cpp]  view plain  copy
 print ?
  1. template <class InputIterator, class Distance>  
  2. inline void advance(InputIterator& i, Distance n)   
  3. {  
  4.     _advance(i, n, iterator_category(i));  
  5. }  
  6. template <class InputIterator, class Distance>  
  7. inline void _advance(InputIterator& i, Distance n, input_iterator_tag)  //第一个函数  
  8. {  
  9.     while (n--) ++i;  
  10. }  
  11. template <class BidirectionalIterator, class Distance>  
  12. inline void _advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag)  //第二个函数  
  13. {  
  14.     if (n >= 0)  
  15.         while (n--) ++i;  
  16.     else  
  17.         while (n++) --i;  
  18. }  
  19. template <class RandomAccessIterator, class Distance>  
  20. inline void _advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag) //第三个函数  
  21. {  
  22.     i += n;  
  23. }  

       到此,STL中重载函数的运用介绍的差不多了,不过有两个疑问。(1)_advance函数提供了三个重载版本,为什么没有定义inline void _advance (InputIterator& i, Distance n, forward_iterator_tag) 这个函数呢?(2)如何获取迭代器的类型呢?即上面的iterator_category(i)调用如何实现的。

       对于第一个疑问,我们知道单链表的迭代器类型是forward_iterator,_advance貌似没有可选的函数以供调用。其实这与STL中迭代器的分类有关。下面是迭代器分类的关系表示,通过为迭代器设计标签类实现。_advance函数正是通过这些标签类激活重载的。代码如下:

[cpp]  view plain  copy
 print ?
  1. //迭代器各类间的关系  
  2. struct input_iterator_tag {};  
  3. struct output_iterator_tag {};  
  4. struct forward_iterator_tag : public input_iterator_tag {};  
  5. struct bidirectional_iterator_tag : public forward_iterator_tag {};  
  6. struct random_access_iterator_tag : public bidirectional_iterator_tag {};  

       从上面定义的关系,我们发现forward_iterator_tag继承自Input_iterator_tag,因此对于forward_iterator类的迭代器,其标签为forward_iterator_tag,通过向上的转换,就可以调用标签为Input_iterator_tag的那个重载版本。可以写个程序来验证一下。

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. //迭代器分类,首字符大写,与标准库区分一下  
  5. struct Input_iterator_tag {};  
  6. struct Output_iterator_tag {};  
  7. struct Forward_iterator_tag : public Input_iterator_tag {};  
  8. struct Bidirectional_iterator_tag : public Forward_iterator_tag {};  
  9. struct Random_access_iterator_tag : public Bidirectional_iterator_tag {};  
  10.   
  11. template <class I>  
  12. void foo(I& i, Input_iterator_tag)   
  13. {  
  14.     cout<<"Input_iterator_tag version"<<endl;  
  15. }  
  16. template <class I>  
  17. void foo(I& i, Bidirectional_iterator_tag)   
  18. {  
  19.     cout<<"Bidirectional_iterator_tag version"<<endl;  
  20. }  
  21. template <class I>  
  22. void foo(I& i, Random_access_iterator_tag)   
  23. {  
  24.     cout<<"Random_access_iterator_tag version"<<endl;  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     int *x;  
  30.     foo(x, Input_iterator_tag());          //输出为Input_iterator_tag version  
  31.     foo(x, Forward_iterator_tag());        //输出为Input_iterator_tag version  
  32.     foo(x, Bidirectional_iterator_tag());  //输出为Bidirectional_iterator_tag version  
  33.     foo(x, Random_access_iterator_tag());  //输出为Random_access_iterator_tag version  
  34.     return 0;  
  35. }  

        对于第二个疑问,好像遇到过,是在介绍模板特化中提到的。用了内嵌型别、模板特化的技术。STL还是运用这些技术解决这个问题的。扩展之前的萃取剂就可以了。下面给出了萃取剂新的定义,同时给出了测试用例,已在VS2008下测试通过。

       至于内嵌型别及模板特化的具体介绍,详见STL运用的C++技术(2)——模板特化

[cpp]  view plain  copy
 print ?
  1. #include <vector>  
  2. #include <list>  
  3. #include <deque>  
  4. #include <iostream>  
  5. using namespace std;  
  6.   
  7. //萃取剂  
  8. template<class I>  
  9. struct Iterator_traits{  
  10.     typedef typename I::value_type value_type;  
  11.     typedef typename I::iterator_category iterator_category; //迭代器的类型  
  12. };  
  13. //特化 原生指针  
  14. template<class T>  
  15. struct Iterator_traits<T*>{  
  16.     typedef T value_type;  
  17.     typedef random_access_iterator_tag iterator_category;  
  18. };  
  19. //特化 原生常指针  
  20. template<class T>  
  21. struct Iterator_traits<const T*>{  
  22.     typedef T value_type;  
  23.     typedef random_access_iterator_tag iterator_category;  
  24. };  
  25.   
  26. #define VALUE_TYPE(I) Iterator_traits<I>::value_type()  
  27. #define ITERATOR_CATEGORY(I) Iterator_traits<I>::iterator_category()   
  28.   
  29. //自定义的advance函数,与STL差不多  
  30. template <class InputIterator, class Distance>  
  31. inline void MyAdvance(InputIterator &i, Distance n)   
  32. {  
  33.     _MyAdvance(i, n, ITERATOR_CATEGORY(InputIterator)); //萃取迭代器的类型  
  34. }  
  35. template <class InputIterator, class Distance>  
  36. inline void _MyAdvance(InputIterator& i, Distance n, input_iterator_tag)   
  37. {  
  38.     while (n--) ++i;  
  39.     cout<<"InputIterator"<<endl;  
  40. }  
  41. template <class BidirectionalIterator, class Distance>  
  42. inline void _MyAdvance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag)   
  43. {  
  44.     if (n >= 0)  
  45.         while (n--) ++i;  
  46.     else  
  47.         while (n++) --i;  
  48.     cout<<"BidirectionalIterator"<<endl;  
  49. }  
  50. template <class RandomAccessIterator, class Distance>  
  51. inline void _MyAdvance(RandomAccessIterator& i, Distance n, random_access_iterator_tag)   
  52. {  
  53.     i += n;  
  54.     cout<<"RandomAccessIterator"<<endl;  
  55. }  
  56. //测试程序  
  57. int main()  
  58. {  
  59.     vector<int> v;  
  60.     v.push_back(1);  
  61.     v.push_back(2);  
  62.     list<int> l;  
  63.     l.push_back(1);  
  64.     l.push_back(2);  
  65.     deque<int> d;  
  66.     d.push_back(1);  
  67.     d.push_back(2);  
  68.   
  69.     vector<int>::iterator iter1 = v.begin();  
  70.     list<int>::iterator iter2 = l.begin();  
  71.     deque<int>::iterator iter3 = d.begin();  
  72.     MyAdvance(iter1, 1); //vector的迭代器是原生指针,因此是RandomAccessIterator  
  73.     MyAdvance(iter2, 1); //链表的迭代器是双向的,因此是BidirectionalIterator  
  74.     MyAdvance(iter3, 1); //双端队列支持随机读写,因此是RandomAccessIterator  
  75.     return 0;  
  76. }  
        本人享有博客文章的版权,转载请标明出处 http://blog.csdn.net/wuzhekai1985
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值