C++学习笔记(八)----表(list)的实现


原文地址: http://www.cnblogs.com/heqile/archive/2011/11/02/2233669.html

作者信息: Email:[email protected]


接下来,我们根据《数据结构和算法分析 C++描述》中图3-12至3-20的代码,继续回顾类的有关知识.

代码如下:

template<typename Object>
class List
{
private:
    struct Node
    {
        Object  data;
        Node   *prev;
        Node   *next;

        Node( const Object & d = Object( ), Node *p = NULL, Node *n = NULL )
          : data( d ), prev( p ), next( n ) { }
    };

public:
    class const_iterator
    {
      public:
        const_iterator( ) : current( NULL )
          { }
    
        const Object & operator* ( ) const
          { return retrieve( ); }
            
        const_iterator & operator++ ( )
        {
            current = current->next;
            return *this;
        }
    
        const_iterator operator++ ( int )
        {
            const_iterator old = *this;
            ++( *this );
            return old;
        }
    
        bool operator== ( const const_iterator & rhs ) const
          { return current == rhs.current; }
        bool operator!= ( const const_iterator & rhs ) const
          { return !( *this == rhs ); }
    
      protected:
        Node *current;
    
        Object & retrieve( ) const
          { return current->data; }
    
        const_iterator( Node *p ) : current( p )
          { }
            
        friend class List<Object>;
    };


    
    class iterator : public const_iterator
    {
      public:
        iterator( )
          { }
    
        Object & operator* ( )
          { return retrieve( ); }
        const Object & operator* ( ) const
          { return const_iterator::operator*( ); }
            
        iterator & operator++ ( )
        {
            current = current->next;
            return *this;
        }
    
        iterator operator++ ( int )
        {
            iterator old = *this;
            ++( *this );
            return old;
        }
    
      protected:
        iterator( Node *p ) : const_iterator( p )
          { }
    
        friend class List<Object>;
    };

public:
    List( )
      { init( ); }
    
    ~List( )
    {
        clear( );
        delete head;
        delete tail;
    }
    
    List( const List & rhs )
    {
        init( );
        *this = rhs;
    }
    
    const List & operator= ( const List & rhs )
    {
        if( this == &rhs )
            return *this;
        clear( );
        for( const_iterator itr = rhs.begin( ); itr != rhs.end( ); ++itr )
            push_back( *itr );
        return *this;
    }
    
    

    iterator begin()
    {
        return iterator(head->next);
    }
    const_iterator begin() const
    {
        return const_iterator(head->next);
    }
    iterator end()
    {
        return iterator(tail);
    }
    const_iterator end() const
    {
        return const_iterator(tail);
    }

    bool empty() const
    {
        return theSize==0;
    }
    int size() const
    {
        return theSize;
    }

    void clear()
    {
        while(!empty())
        pop_front();
    }

    Object & front( )
      { return *begin( ); }
    const Object & front( ) const
      { return *begin( ); }
    Object & back( )
      { return *--end( ); }
    const Object & back( ) const
      { return *--end( ); }
    void push_front( const Object & x )
      { insert( begin( ), x ); }
    void push_back( const Object & x )
      { insert( end( ), x ); }
    void pop_front( )
      { erase( begin( ) ); }
    void pop_back( )
      { erase( --end( ) ); }

    // Insert x before itr.
    iterator insert( iterator itr, const Object & x )
    {
        Node *p = itr.current;
        theSize++;
        return iterator( p->prev = p->prev->next = new Node( x, p->prev, p ) );
    }

    // Erase item at itr.
    iterator erase( iterator itr )
    {
        Node *p = itr.current;
        iterator retVal( p->next );
        p->prev->next = p->next;
        p->next->prev = p->prev;
        delete p;
        theSize--;
 
        return retVal;
    }
 
    iterator erase( iterator start, iterator end )
    {
        for( iterator itr = from; itr != to; )
            itr = erase( itr );
 
        return to;
    }


  private:
    int   theSize;
    Node *head;
    Node *tail;

    void init( )
    {
        theSize = 0;
        head = new Node;
        tail = new Node;
        head->next = tail;
        tail->prev = head;
    }
};

1.我们看到在第4行,Node类作为List的成员被标记为private,所以它就不能被List之外的类访问,它对用户(类的使用者)来说是隐藏的.同时,定义Node时使用了struct关键字,Node中的成员会默认为是公有的,使我们能在List及其派生类的成员函数中使用.

    2.看两个构造函数,分别在第12行和89行.Node()的形参列表中有两个默认值为NULL的指针,接着通过初始化列表,next和prev成员被安全的初始化了.而在List()中,我们没有看到形参也没有看到初始化列表.这样,在List()的初始化阶段,head和tail是危险的,他们没有被初始化;但是函数体中的new语句保证了构造函数的正确性,new语句返回的地址被赋值给head和tail.

    3.const_iterator类有两个构造函数,分别在第19行和第49行,其中const_iterator()是公有的,而const_iterator(Node *p)如果也被标记为public是不合适的,因为Node类对用户是隐藏的.而事实上定义这样一个构造函数就只是在List类范围内使用的.

    4.关于派生类的构造函数.当系统调用第60行的默认构造函数iterator()时,会自动调用其基类的默认构造函数,用来初始化从const_iterator继承而来的成员current.如果执行下面这句代码:

List<int> lis;

List<int>::iterator iter=lis.begin();

第2句的执行过程是这样的:首先调用117行的begin()函数,然后调用其函数体中的iterator( Node *p )函数(定义在第82行),接着调用其初始化列表中的const_iterator( Node *p )函数(定义在第49行).这样构造了一个构造了一个iterator类型的临时对象,该对象的current成员的值是复制了lis的头结点的next成员的值.最后使用编译器合成的iterator类的复制构造函数将这个临时对象复制给iter.

可以同样想一想并且测试下面的语句:

List<int>::iterator iter;

是怎样执行的.

    5.第25行至第36行是const_iterator类对"++"操作符的重载,我们知道"++"操作符有两个版本,分别是前缀和后缀版本,这两个版本是根据形参表中是否有一个匿名的int参数来区分的.如果形参表为空,则为前缀版本(++itr);而后缀版本(itr++)调用单参数operator++.这个int参数实际是不使用的,仅仅作为一个标识而存在.

    设计const_iterator是希望有一种"自以为"指向const对象的指针,这种指针不能修改其所指向的对象,但是这种指针本身的值是可以改变的.我们看到对"++"的重载版本不是const函数,而且这个函数返回的是一般引用.似乎iterator类也可以继承这对函数,但是由于返回值类型不同,所以在iterator类的定义中我们得重新提供operator++的实现.

    6.为了实现设计"const_”类的目的,我们在第22行重载”*”操作符时,返回的是const引用,这就保证了无法使用const_iterator类对象修改其所指向对象的值.

进一步探究这个函数,它是一个const函数,因为我们不打算通过它修改const_iterator类对象本身的值,所以函数体内的retrieve( )函数(定义在第46行)也必须是const函数.这是因为:const成员函数不能访问非const成员函数,反之则可以.

    显然,在定义iterator类时,我们要重写operator*函数(第63行),这个函数返回一般引用,因此可以用来修改iterator对象所指向的对象的值.函数体中同样调用了retrieve()函数,如前所述,非const成员函数可以访问const成员函数,所以这是合法的.

    那么为什么还要写第65,66行的代码呢?因为对于iterator类来说,由于有了修改版本的operator*函数(第63,64行),其基类中的operator*函数(第22,23行)就会被隐藏掉.这时,如果我们对一个由const关键字修饰的iterator对象解引用,编译器就会找不到可用的operator*函数.因此,第65,66行的代码是必须的.


你可能感兴趣的:(C++学习笔记(八)----表(list)的实现)