STL源码剖析学习笔记(二)

iterator和traits编程技法(上)

STL的中心思想在于,将数据容器和算法分开,彼此独立设计,然后再用一个粘合剂将其黏在一起。这个粘合剂,就是iterator。
由于容器和算法都是适应于泛型编程的,所以iterator也必须适应这方面的技术。

例如find(),它接受两个迭代器和一个“搜寻目标”

template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value) {
    while(first != last && *first != value)
        ++first;
    return first;
}

给定不同的迭代器,那么find就可以针对不同的容器进行查找操作。
迭代器是一种智能指针,他针对指针中最常见的内容提领(dereference)和成员访问(member access),这里隐含的一个重要的消息就是迭代器必须包含原生指针,即迭代器是原生指针的一种拓展。所以,迭代器最重要的就是重载operator*和operator->。

举个例子,我们来模拟为一个单向链表list设计Iterator

链表

template <typename T>
class List
{
    void insert_front(T value);
    void insert_end((T value);
    void display(std::ostream &os = std::cout) const;
    //.......
private:
    ListItem* _end;
    ListItem* _front;
    long _size;
};
template <typename T>
class ListItem
{
    public:
    T value() const { return _value; }
    ListItem * next() const { return _next; }
    private:
    T _value;
    ListItem* _next;
};

链表的Iterator实现

template <class Item>
struct ListIter
{
    Item * ptr;
    ListIter(Item *p = 0):ptr(p){ }//default creator
    Item& operator *() const { return *ptr; }
    Item* operator->() const { return ptr; }
    ListIter& operator++()
    { ptr = ptr->next() ;return *this; }
    ListIter operator++(int)
    { ListIter tmp = *this; ++*this; return tmp; }
    bool operator== (const ListIter& i) const
    { return ptr == i.ptr; }
    bool operator!=(const ListIter& i)const
    { return ptr != i.ptr; }
};

用iterator将List和find()链接起来:

void main()
{
    List<int> mylist;
    for(int i=0; i<5;++i){
        mylist.insert_front(i);
        mylist.insert_end(i+2);
    }
    mylist.display(); //4 3 2 1 0 2 3 4 5 6
    ListIterint>> begin(mylist.front());
    ListIterint>>end;
    ListIterint>>iter;

    iter = find(begin, end, 3);
    if (iter == end)
        cout<<"3 not found in list"<else
        cout<<"found."<value()<

由于find函数以 *iter!=value来检测元素是否吻合,所以还要写以下的全局函数

template <typename T>
bool operator!=(const ListItem& item, T n)
{ return item.value() != n; }

以上可以看出,这样写暴露了太多,首先为了制造 begin和end迭代器,我们暴露了ListItem;然后为了迭代器的++操作,我们又暴露了ListItem的next成员变量。也就是说,如果不是为了Iterator,ListItem应该是完全包含在List内部的,这个数据结构的节点不应该被暴露。
所以,我们干脆让容器实现自己的iterator好了
这样看起来很合理。这就是为什么STL里,所有的容器都提供自己的专属迭代器的原因。

现在,问题在于,如果我们写迭代器里面的一些算法,不可避免的要用到迭代器所指的类型(其实还包括其他各种类型),如果我们要声明一个这个类型的一个变量,这个类型我们怎么取得?例如上例中的,迭代器所指之物的类型,就是int。C++并不支持用typeof()来判定一个元素的类型。即是用 typeid(),获得也是类型名称,不能拿来声明变量。
解决方法:利用函数模板的参数堆导机制。
例如:

template<class I,class T>
void func_impl( I iter, T t )
{
    T tmp;//比方说,这个就是我们想要的int
};
template<class I>
inline
void func(I iter)
{
    func_impl(iter, *iter);
}
int main()
{
    int i;
    func(&i);
}

这段代码怎么理解呢,就是说,当我们调用func的时候,会调用func_impl,而编译器会对func_impl这个函数模板的参数类型进行自动推导,即推导出来,T就是int。
套用到迭代器的话,func的参数只用填迭代器就行,func_impl函数会将迭代器所指对象的类型推导出来,我们就可以直接用(例子中的T)。
刚才说迭代器所指类型,只是一种迭代器的类型,称作迭代器的value_type。
上述函数模板参数推导机制,只能推导参数,如果value_type必须用作函数的返回值,则这个方法就不能用了,具体要用什么方法。我们待续

下一篇:traits编程技法的思想,就是利用内嵌类型以及template参数推导功能,增强C++关于类型认证方面的能力。

你可能感兴趣的:(STL源码深入研究)