窥探迭代器(iterator)的真相

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值