【c++】STL源码剖析:迭代器与traits编程技巧

参考书籍《STL源码剖析》

STL主要学以下几部分:
一.容器
        1.顺序容器
            vector  list  deque
        2.关联容器  红黑树  BST  
            set multiset  map  multimap //观察者模式
        3.容器适配器 deque
            stack   queue  priority_queue
二.泛型算法
        find sort  
三.迭代器
        容器:  iterator  reverse_iterator
               const_iterator  const_reverse_iterator
        五种迭代器
               输入型   输出型   正向  双向  随机访问
        三种
            1.反向
            2.插入型
            3.流式
四.函数对象 仿函数  //greater  less     
        二种
            1.一元函数对象
            2.二元函数对象
五.适配器  bind2nd   not1
            
六.空间配置器   
        1.一级空间配置器
        2.二级空间配置器

----------------------------------------

零、前言:本片博客想写什么?

跟着《STL源码剖析》的思路,自己设计list和vector中的迭代器,遇到问题,解决问题,之后总结,再剖析源码。

但要从了解模板,因为STL中运用了大量的模板,模板的概念简单使用,模板特例化(部分特例化,完全特例化);迭代器的类型,几种迭代器的关系,剖析源码中的迭代器,萃取traits编程。。。

书中的总结:多读几遍去理解:

        设计迭代器的相应型别,是迭代器的责任。
        设计适当的迭代器,是容器的责任。
        只有容器本身,才知道该设计出怎样的迭代器来走访自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员)。
        至于算法,完全可以独立于容器的迭代器之外自行发展,只要设计时以迭代器为对外接口就行。  
        traits编程技法大量运用在STL中。它利用“内嵌性别”的编程技巧与编译器template参数推导功能,增强c++未能提供的关于型别认证的能力,
        弥补c++不为强型别语言的缺憾。
        了解traits编程,是打开STL源码剖析的钥匙。

一、几种迭代器的关系:


1.迭代器的相应型别:

“什麼是迭代器相应型别?答:迭代器所指之物的型別便是其一(即迭代器所指对象的类型)”

迭代器的相应型别有以下5种:

value_type                  值类型  :迭代器所指对象的型别
difference_type          差值类型:用来表示两个迭代器之间的距离
reference_type           引用类型:
pointer_type                指针类型 const_pointer_type 常指针类型
iterator_category         迭代器类型 (分为下面的5种类型)

《STL源码剖析》这本书是台湾同胞所著,所以书中一些词语读起来感觉很别扭。
比如“对象的类型”,书中即为“对象的型别”。

  

2.五种迭代器

根据迭代器移动特性與施行动作,迭代器被分为五类:
        输入型迭代器    input_iterator
        输出型迭代器    output iterator
        正向迭代器        Forward Iterator
        双向迭代器        Bidirectional Iterator
        随机迭代器        Random Access Iterator

5种迭代器的关系: 如图《STL源码剖析》126页 


   图中箭头表示继承关系,可以看到这五种迭代器的层层继承关系。

(《高效c++编程》第二版 继承 什么是共有继承?“是一个”的意思!什么是私有继承?  “组合”......)

3. 容器中的迭代器:

    hashtable slist:    正向迭代器
    list map set   :    双向迭代器 
    vector dequeue:随机迭代器 
(这里好理解,容器中的迭代器就是5种迭代器其中的3个,因为正向迭代器继承了输入和输出型迭代器,所以这三中就够了。)

(还是那句话:“设计迭代器的相应型别,是迭代器的责任。 设计适当的迭代器,是容器的责任。”)
 

二、自己设计迭代器:

1.定义itreator作为标记用的类别

//自己创建头文件iterator.h 

struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };

   
2.定义迭代器类型:

        //写在iterator.h中
        /* 下面模板中简写了模板类型:
        _Cate : iterator_category         迭代器类型 (分为下面的5种类型)
        _Ty: value_type                  值类型  :迭代器所指对象的型别
        _Dist:difference_type          差值类型:用来表示两个迭代器之间的距离
        _P : pointer_type                指针类型 const_pointer_type 常指针类型
        _ref:reference_type           引用类型:
        */
        template<class _Cate,class _Ty,class _Dist class _P = _Ty*,class _Ref = _Ty&> //定义迭代器需要5个相应型別:迭代器类型 值 差值 指针 引用                                                                              
        struct iterator
        {
            //迭代器5个相应型别的重命名 typedef :
            typedef _Cate iterator_category;
            typedef _Ty   value_type;
            typedef _Dist difference_type;
            typedef _P    pointer;
            typedef _Ref  reference;
        }    

3.定义容器中的迭代器:双向 随机 正向:

设计3个class,共有继承iterator类,并给出迭代器的标志

        //写在iterator.h中
        template<class _Ty,class _Dist> //正向迭代器
        class _Forit:public iterator<forward_iterator_tag,_Ty,_Dist>
        {};
        
        template<class _Ty,class _Dist>  //双向迭代器
        class _BIDrit:public iterator<bidirectional_iterator_tag,_Ty,_Dist>
        {};
        
        template<class _Ty,class _Dist>   //随机迭代器
        class _Ranrit:public iterator<random_access_iterator_tag,_Ty,_Dist>
        {};

    
4.在设计自己的list 

当然这里没写全,只涉及到迭代器的内容;还缺很多函数:插入,删除,构造......

        //写在zdw_list.h 下
        namespace zdw
        {
            template<class _Ty>
            class list //设计一个链表 ,结点类,迭代器类
            {
            private:
                struct _node;
                typedef _node* nodeptr;
                struct _node
                {
                    nodeptr pre;
                    nodeptr next;
                    _Ty value;   //结点的值
                };

            public:
                class const_iterator:public zdw::_Bidit<_Ty,int> //继承双向迭代器
                {};
                class iterator:public const_iterator  
                {};
            };  
        }

这里写在namespace中是为了防止命名污染,使用自己的命名。

4.在设计自己的 vector

        //zdw_vector.h文件 设计一个vector 
        namespace zdw
        {
            template<class _Ty>
            class vector
            {
            public:
                class const_iterator:public zdw::_Ranrit<_Ty,int>  //随机访问迭代器
                {};
                class iterator:public const_iterator
                {};
            };
        }


6.设计迭代器的advance函数: 迭代器的移动

思考:再写advance 如何得到迭代器类型?   这就是书中的重点:类型萃取traits编程技巧

根据不同的容器,迭代器的移动也是不一样的:

正向迭代器(比如单链表):需要一步一步移动 

双向迭代器(比如双链表):判断正反,前移动还是后移动

随机迭代器(比如vector 数组):一步到位直接跨过去

//在iterator.h中 
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,input_iterator_tag)//输入型迭代器:
{
	 while (n--) ++i;//只能一个一个走
}
            
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,bidirectional_iterator_tag)//双向迭代器
{
	if (n >= 0)
	while (n--) ++i;  //要判断正负值
	 else
	while (n++) --i;
}
            
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,random_accessl_iterator_tag)//随机迭代器
{
	i += n;      //随机迭代器可以直接+
}
	

但是最终的advance设计就是一个难点:
设计一:解决不了问题,error,pass掉

            template<class _II,class _Dist>
            void advance(_II &i,_Dist n) //这个函数的设计 重点难点
            {
                //advance 如何调用_advance(_II &i,_Dist n,input_iterator_tag)双向迭代器的移动函数?
            }
            

设计二:将迭代器类型当做类型传参,但是还有问题


            template<class _II,class _Dist>        //类型加() 表示这是一个对象
            void advance(_II &i,_Dist n,input_iterator_tag() ) //但是,系统如何根据_II迭代器类型从而知道它是input_iterator_tag
            {
                _advance(i,n,input_iterator_tag())
            }


设计三://这是今天将的重点 :类型萃取 这里先不写结果,需要一步一步分析


7.写一个类型萃取的实例    :

我理解的类型萃取:就是要拿到template的类型,如何拿到? 加一个中间层,还是那个原理,“计算机中的问题可以加一个中间层,这里的中间层就是萃取萃取结构体)

(1)一个不够完美的程序:不能做到接口复用:

template<class _Ty>
class SeqList
{
public:
	typedef _Ty value_type;
private:
	_Ty data[10];
	int cursize;
public:
	SeqList():cursize(0){};
	~SeqList(){};
	void push(const _Ty &x)
	{
		data[cursize++] = x;
	}
};

template<class Container>
void Append(Container &seq)
{	
	int x;
	cin>>x;
	seq.push(x);
}

int main()
{
	SeqList<int> seq;
	Append(seq);
}

 这个程序有个问题就是:如果我们SeqList<double> seq;那么Append函数中的x就需要改为double。

(2)增加类型萃取机制:

        template<class _Ty>
        class SeqList   //我们设计一个顺序表
        {
        public:
            typedef _Ty value_type;
        private:
            _Ty data[10];
            int cursize;
        public:
            SeqList():cursize(0){};
            ~SeqList(){};
            void push(const _Ty &x)
            {
                data[cursize++] = x;
            }
        };
        
        template<class Container> //中间层的萃取机制,拿到当前容器中的数据类型
        struct traits_seq
        {
            typedef typename Container::value_type value_type;
        };
        
        template<class Container>
        void Append(Container &seq)
        {
            typename traits_seq<Container>::value_type x;
            cin>>x;
            seq.push(x);
        }
        
        int main()
        {
            SeqList<int> seq;
            Append(seq);
        }

8.回到iterator中advance函数的萃取

    template<class Iterator>
    struct itreator_traits
    {
        typedef typename  Iterator::iterator_category       iterator_category;
        typedef typename  Iterator::value_type              value_type;
        typedef typename  Iterator::difference_type         difference_type;
        typedef typename  Iterator::pointer                  pointer;
        typedef typename  Iterator::reference                reference;
    }

    
9.写迭代器的方法:重载++ -- 前置后置等

    
10.解决普通指针不能萃取的问题

原生类型指针:是一种随机迭代器
 指针的处理:模板函数的特化 部分特化 完全特化

        //特化的几个示例:
            template<class T>
            class C{};           //无特化
            
            template<class T>
            class C<T*>{};       //部分特化
            
            template<class T>
            class C<const T*>{}; //部分特化
            
            template<>
            class C<char *>{}; //完全特化

关于模板特例化的形象说明:老师当时这样讲,让我印象深刻

模板特例化就像找对象:

完全特化就是刚毕业的小年轻们,“我要娶白富美,乖巧可爱的迪丽热巴”,

部分特化就是“我要找漂亮的就行”;

无特化就是:“我娶个女的,就行!”
        

        //重新写萃取结构体
        template<class T>
        struct iterator_traits<const T*>
        {
            iterator_traits(){};
            typedef random_access_iterator_tag iterator_category;
            typedef T                          value_type;
            typedef int                        difference_type;
            typedef T*                         pointer;
            typedef T&                          reference;
        }


    11.sgi的做法 和 vs的做法
        
    12.distance函数的实现 :求得两个迭代器之间的举例

        template<class _II>
        typename iterator_traits<_II>::difference_type
        __distance(_II _First,_II _Last,input_iterator_tag)     //输入迭代器
        {
            typename iterator_traits<_II>::difference_type n = 0;
            for(,_First != Last;++_First,++n);
            return n;
        }
        
        template<class _RAI>
        typename iterator_traits<_RAI>::difference_type
        __distance(_RAI _First,_RAI _Last,random_access_iterator_tag) //随机迭代器
        {
            return _Last - _First;
        }
    
        template<class _II>
        typename iterator_traits<_II>::difference_type
        distance(_II _First,_II _Last)      //最终distance
        {
            return __distance(_First,_Last,iterator_category(_First));
        }


11.总结:
        设计迭代器的相应型别,是迭代器的责任。
        设计适当的迭代器,是容器的责任。
        只有容器本身,才知道该设计出怎样的迭代器来走访自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员)。
        至于算法,完全可以独立于容器的迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
        traits编程技法大量运用在STL中。它利用“内嵌性别”的编程技巧与编译器template参数推导功能,增强c++未能提供的关于型别认证的能力,
        弥补c++不为强型别语言的缺憾。
        了解traits编程,是打开STL源码剖析的钥匙。


12.iterator完全源码:


      

 // 节录自 SGI STL <stl_iterator.h>
        // 五种迭代器类型
        struct input_iterator_tag {};
        struct output_iterator_tag {};
        struct forward_iterator_tag : public input_iterator_tag {};
        struct bidirectional_iterator_tag : public forward_iterator_tag {};
        struct random_access_iterator_tag : public bidirectional_iterator_tag {};
        
        // 为避免写代码时挂一漏万,自行开发的迭代器最好继承自下面这个 std::iterator
        template <class Category, class T, class Distance = ptrdiff_t,class Pointer = T*, class Reference = T&>
        struct iterator 
        {
            typedef Category iterator_category;
            typedef T value_type;
            typedef Distance difference_type;
            typedef Pointer pointer;
            typedef Reference reference;
        };
        
        // 「榨汁机」 traits
        template <class Iterator>
        struct iterator_traits 
        {
            typedef typename Iterator::iterator_category iterator_category;
            typedef typename Iterator::value_type value_type;
            typedef typename Iterator::difference_type difference_type;
            typedef typename Iterator::pointer pointer;
            typedef typename Iterator::reference reference;
        };
        
        // 针对原生指针( native pointer)而设计的 traits 偏特化版(部分特化)。
        template <class T>
        struct iterator_traits<T*> 
        {
            typedef random_access_iterator_tag iterator_category;
            typedef T value_type;
            typedef ptrdiff_t difference_type;
            typedef T* pointer;
            typedef T& reference;
        };

        // 针对原生之 pointer-to-const 而设计的 traits 偏特化版(部分特化)。 台湾同胞将部分特化称之为偏特化
        template <class T>
        struct iterator_traits<const T*> 
        {
            typedef random_access_iterator_tag iterator_category;
            typedef T value_type;
            typedef ptrdiff_t difference_type;
            typedef const T* pointer;
            typedef const T& reference;
        };
        
        // 这个函式可以很方便的决定某个迭代器的类型( category)
        template <class Iterator>
        inline typename iterator_traits<Iterator>::iterator_category
        iterator_category(const Iterator&) 
        {
            typedef typename iterator_traits<Iterator>::iterator_category category;
            return category();
        }
        
        // 这个函式可以很方便的决定某个迭代器的 distance type
        template <class Iterator>
        inline typename iterator_traits<Iterator>::difference_type*  
        distance_type(const Iterator&) 
        {
            return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
        }
        
        // 这个函式可以很方便的决定某个迭代器的value type
        template <class Iterator>
        inline typename iterator_traits<Iterator>::value_type*
        value_type(const Iterator&) 
        {
            return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
        }
        
        // 以下是整组 distance 函数
        template <class InputIterator>
        inline iterator_traits<InputIterator>::difference_type
        __distance(InputIterator first, InputIterator last,input_iterator_tag) 
        {
            iterator_traits<InputIterator>::difference_type n = 0;
            while (first != last) 
            {
                ++first; ++n;
            }
            return n;
        }
        
        template <class RandomAccessIterator>
        inline iterator_traits<RandomAccessIterator>::difference_type
        __distance(RandomAccessIterator first, RandomAccessIterator last,random_access_iterator_tag) 
        {
            return last - first;
        }
        
        template <class InputIterator>
        inline iterator_traits<InputIterator>::difference_type
        distance(InputIterator first, InputIterator last) 
        {
            typedef typename
            iterator_traits<InputIterator>::iterator_category category;
            return __distance(first, last, category());
        }
        
        // 以下是整组 advance 函式
        template <class InputIterator, class Distance>
        inline void __advance(InputIterator& i, Distance n,input_iterator_tag) 
        {
            while (n--) ++i;
        }
        
        template <class BidirectionalIterator, class Distance>
        inline void __advance(BidirectionalIterator& i, Distance n,bidirectional_iterator_tag) 
        {
            if (n >= 0)
            while (n--) ++i;
            else
            while (n++) --i;
        }
        
        template <class RandomAccessIterator, class Distance>
        inline void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag) 
        {
            i += n;
        }
        template <class InputIterator, class Distance>
        inline void advance(InputIterator& i, Distance n)
        {
            __advance(i, n, iterator_category(i));
        }


--------------------------------------------
 
C++思想:模拟现实,模拟的及其相似!编程,是在计算机中反映世界!
 

发布了89 篇原创文章 · 获赞 68 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zDavid_2018/article/details/89308881