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;
};
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; }
};
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++关于类型认证方面的能力。