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

转载 2016年05月31日 10:59:24

 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。下面给出了代码示例,整理了一下。

  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函数,为的是提高效率。

  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函数正是通过这些标签类激活重载的。代码如下:

  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的那个重载版本。可以写个程序来验证一下。

  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)——模板特化

  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

相关文章推荐

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

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

STL运用的C++技术(5)——重载操作符

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

C++运算符重载函数基础及其值返回状态

运算符重载是C++的重要组成部分,它可以让程序更加的简单易懂,简单的运算符使用可以使复杂函数的理解更直观。   对于普通对象来说我们很自然的会频繁使用算数运算符让他们参与计算,但是对于自定义类...

C++继承(6) - 隐藏基类中的所有重载函数

C++中,如果子类重新定义了基类中的成员函数,则基类中所有相同函数名的函数都会被隐藏起来。

C++基础---重载函数

1. 重载函数 1.1 重载函数的定义 重载函数:是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不...

我对C++中重载函数的反思

记得之前总结C++学习心得的时候,我在函数重载的地方有一句话是函数的重载不能通过返回值来确定,但是有例外,我当时没有想起那个特例,今天在学习JAVA的时候看到JAVA里面函数返回值可以区分函数重载的时...

c++之-----重载函数overload function

摘自:c++primer    重载函数(overloadedfunction)是C++支持的一种特殊函数,C++编译器对函数重载的判断更是C++语言中最复杂的内容之一    首先我们先明确一下重...

为什么C++赋值运算符重载函数不能被继承?

这个问题曾经困扰过我一阵子。请先看一下下面的源代码: class A1 { public:          int perator=(int a)      ...

C++重载函数分析

C++重载函数的定义:同一作用域的多个函数,如果具有相同函数名而同时具有不同的形参列表,它们可以称为重载函数。

C++ 重载函数调用运算符

重载函数调用运算符
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)