在前面的文章中,我们曾自定义了一个迭代器类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;
}