在前面的文章中,我们曾自定义了一个迭代器类line_iterator,不过它并不具有通用性。现在借助于模板,我们来定义一个模板化的迭代器。仍然延续线性查找的思路,首先将SimpleFind()函数改写成函数模板tFind(),代码如下所示:
template<typename T> T* tFind(T* start, T* end, T val) { while(start != end && *start != val) ++start; return *start == val ? start : (T*)0; }
然而,tFind()只能接受内建指针类型,通用性仍然受限。为了扩展tFind()的适用范围,进一步将tFind()改写为:
template<typename Iterator, typename T> Iterator tFind(Iterator start, Iterator end, const T& val) { while(start != end && *start != val) ++start; return start != end ? start : (Iterator)0; }
在tFind()函数模板中,除数据类型T之外,我们还引入了一个Iterator类型。当然Iterator是支持T*的,但它的范围远比内建指针来的广泛。实际上,只要Iterator能够支持tFind()内部调用的operator!=,operator++等操作符方法就成。之前我们自定义的line_iterator不就是这样做的吗?(如果你不知道line_iterator,建议你先大致看看之前的那篇文章)
现在,提出一个新的要求:对一个单链表数据结构对象进行线性查找。我们知道,单链表中的各个节点是利用next指针来串联的,因此对链表上某节点node的指针做++操作时,不能简单的指针加1,而是this->next。如何实现这样的行为?操作符重载正符合我们的要求。
但是,我们却不能对内建指针node*类型重载operator++,因为那样做将会改变operator++的语义,C++也不会允许你那样做。但如果我们将operator++放入自定义class中,成为成员函数的角色,那就没有什么特别限制了。也就是说,我们可以对class的成员操作符operator++进行重载。而这里所说的class,其实就是iterator class。
好,下面就来实现上述想法。首先实现单链表,比较容易,只要定义好节点的struct并在I/O操作时按链表方式来实现:
// list: roll_list typedef struct Student { int num; char name[10]; bool check; struct Student* next; }Student,*roll_list;
我们定义一个学生结构体(Student),其中包含学号、姓名、是否签到三个数据域,另外还有一个next指针,指向单链表的下一个节点。同时又将Student*类型重命名为roll_list,表示点名单。
在主函数中,对链表的操作大概是这样的:(不要迷惑这些代码摆放次序,我会在文章的末尾给出完整的示例代码)
int num; char ch; roll_list tmp = NULL; while(cin>>num && num != 0) { Student* pStu = new Student; pStu->num = num;cin.get(ch); cin.getline(pStu->name,sizeof(pStu->name)); pStu->check = false; pStu->next = tmp; tmp = pStu; }
点名单的tmp是头指针,如果一个学生都没有,则tmp = NULL。在输入学生数据时,采用了头插入法。此外,判断输入结束的标志是输入的学号为0时。
这样我们就成功构建了单链表,正如前面所说,直接调用tFind(tmp,null,n)肯定不行,因为tFind()内部要对指针做++操作,而默认的指针后移在这里行不通。接下来,就来看看迭代器是怎样力挽狂澜的。其实如果你比较熟悉智能指针,你会发现迭代器的思想与智能指针太神似,都是对内建指针的封装,然后实现必要的接口,让用户误以为它就是内建指针。如果你看过设计模式,大概知道这就是代理模式。言归正传,我们来定义这个迭代器:
// list_iterator template<typename T> class list_iterator { T* m_ptr; public: list_iterator():m_ptr(NULL){} list_iterator(T* ptr):m_ptr(ptr){} T& operator*()const { return *m_ptr; } T* operator->()const { return m_ptr; } bool operator==(const list_iterator& rhs) { return m_ptr == rhs.m_ptr; } bool operator!=(const list_iterator& rhs) { return !(m_ptr == rhs.m_ptr); } list_iterator& operator++() { m_ptr = m_ptr->next; return *this; } list_iterator operator++(int) { list_iterator tmp = *this; m_ptr = m_ptr->next; return tmp; } };
这里将list_iterator写成了模板形式,如此一来,它不仅仅支持例子中的Student链表,还支持任何其它含有next指针的链表,从而提高了代码的通用性。前面说到了,list_iterator是对内建指针T* m_ptr的封装,外界使用list_iterator就好象使用T*一样,因为list_iterator的操作符方法使它完全可以以假乱真。现在考虑tFind的调用:
tFind(list_iterator<Student>(tmp),list_iterator<Student>(), num)
传给tFind()的参数包括两个list_iterator<Student>临时对象以及一个int类型的num,在tFind()的内部调用的operator!=、operator++等操作,list_iterator完全可以胜任,因为我们给它定义了operator++等方法。
下面是示例程序的运行结果:
8 马克思 10 玛丽 12 米奇 13 汤姆 20 吉瑞 0 Input a Student Number to Search:10 Find Student: 学号:00010 姓名:玛丽 出席情况:还未签到
示例程序的全部代码如下:
/* tFind.cpp * created: btwsmile * date: 2011-12-18 * remarks: * * input 8 马克思 10 玛丽 12 米奇 13 汤姆 20 吉瑞 0 */ #include<iostream> #include<iomanip> using namespace std; // list: roll_list typedef struct Student { int num; char name[10]; bool check; struct Student* next; }Student,*roll_list; // operaotr==(Student, int) bool operator==(const Student& lhs, int rhs) { return lhs.num == rhs; } bool operator!=(const Student& lhs, int rhs) { return !(lhs.num == rhs); } // list_iterator template<typename T> class list_iterator { T* m_ptr; public: list_iterator():m_ptr(NULL){} list_iterator(T* ptr):m_ptr(ptr){} T& operator*()const { return *m_ptr; } T* operator->()const { return m_ptr; } bool operator==(const list_iterator& rhs) { return m_ptr == rhs.m_ptr; } bool operator!=(const list_iterator& rhs) { return !(m_ptr == rhs.m_ptr); } list_iterator& operator++() { m_ptr = m_ptr->next; return *this; } list_iterator operator++(int) { list_iterator tmp = *this; m_ptr = m_ptr->next; return tmp; } }; // function template: tFind /* template<typename T> T* tFind(T* start, T* end, T val) { while(start != end && *start != val) ++start; return *start == val ? start : (T*)0; } */ template<typename Iterator, typename T> Iterator tFind(Iterator start, Iterator end, const T& val) { while(start != end && *start != val) ++start; return start != end ? start : (Iterator)0; } // main function int main() { int num; char ch; roll_list tmp = NULL; while(cin>>num && num != 0) { Student* pStu = new Student; pStu->num = num;cin.get(ch); cin.getline(pStu->name,sizeof(pStu->name)); pStu->check = false; pStu->next = tmp; tmp = pStu; } cout<<"Input a Student Number to Search:"; cin>>num; list_iterator<Student> t; if((t = tFind(list_iterator<Student>(tmp),list_iterator<Student>(), num)) != NULL) { cout<<"Find Student:\n"; cout<<"学号:"<<setw(5)<<setfill('0')<<t->num<<"\t姓名:"<<t->name<<"\t出席情况:"<<(t->check ? "已经签到" : "还未签到"); } else cout<<"No Such Student in Roll List"; cout<<endl<<endl; system("pause"); return 0; }