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

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

代码如下:

  1 template<typename Object>
  2 class List
  3 {
  4 private:
  5     struct Node
  6     {
  7         Object  data;
  8         Node   *prev;
  9         Node   *next;
 10 
 11         Node( const Object & d = Object( ), Node *p = NULL, Node *n = NULL )
 12           : data( d ), prev( p ), next( n ) { }
 13     };
 14 
 15 public:
 16     class const_iterator
 17     {
 18       public:
 19         const_iterator( ) : current( NULL )
 20           { }
 21     
 22         const Object & operator* ( ) const
 23           { return retrieve( ); }
 24             
 25         const_iterator & operator++ ( )
 26         {
 27             current = current->next;
 28             return *this;
 29         }
 30     
 31         const_iterator operator++ ( int )
 32         {
 33             const_iterator old = *this;
 34             ++( *this );
 35             return old;
 36         }
 37     
 38         bool operator== ( const const_iterator & rhs ) const
 39           { return current == rhs.current; }
 40         bool operator!= ( const const_iterator & rhs ) const
 41           { return !( *this == rhs ); }
 42     
 43       protected:
 44         Node *current;
 45     
 46         Object & retrieve( ) const
 47           { return current->data; }
 48     
 49         const_iterator( Node *p ) : current( p )
 50           { }
 51             
 52         friend class List<Object>;
 53     };
 54 
 55 
 56     
 57     class iterator : public const_iterator
 58     {
 59       public:
 60         iterator( )
 61           { }
 62     
 63         Object & operator* ( )
 64           { return retrieve( ); }
 65         const Object & operator* ( ) const
 66           { return const_iterator::operator*( ); }
 67             
 68         iterator & operator++ ( )
 69         {
 70             current = current->next;
 71             return *this;
 72         }
 73     
 74         iterator operator++ ( int )
 75         {
 76             iterator old = *this;
 77             ++( *this );
 78             return old;
 79         }
 80     
 81       protected:
 82         iterator( Node *p ) : const_iterator( p )
 83           { }
 84     
 85         friend class List<Object>;
 86     };
 87 
 88 public:
 89     List( )
 90       { init( ); }
 91     
 92     ~List( )
 93     {
 94         clear( );
 95         delete head;
 96         delete tail;
 97     }
 98     
 99     List( const List & rhs )
100     {
101         init( );
102         *this = rhs;
103     }
104     
105     const List & operator= ( const List & rhs )
106     {
107         ifthis == &rhs )
108             return *this;
109         clear( );
110         for( const_iterator itr = rhs.begin( ); itr != rhs.end( ); ++itr )
111             push_back( *itr );
112         return *this;
113     }
114     
115     
116 
117     iterator begin()
118     {
119         return iterator(head->next);
120     }
121     const_iterator begin() const
122     {
123         return const_iterator(head->next);
124     }
125     iterator end()
126     {
127         return iterator(tail);
128     }
129     const_iterator end() const
130     {
131         return const_iterator(tail);
132     }
133 
134     bool empty() const
135     {
136         return theSize==0;
137     }
138     int size() const
139     {
140         return theSize;
141     }
142 
143     void clear()
144     {
145         while(!empty())
146         pop_front();
147     }
148 
149     Object & front( )
150       { return *begin( ); }
151     const Object & front( ) const
152       { return *begin( ); }
153     Object & back( )
154       { return *--end( ); }
155     const Object & back( ) const
156       { return *--end( ); }
157     void push_front( const Object & x )
158       { insert( begin( ), x ); }
159     void push_back( const Object & x )
160       { insert( end( ), x ); }
161     void pop_front( )
162       { erase( begin( ) ); }
163     void pop_back( )
164       { erase( --end( ) ); }
165 
166     // Insert x before itr.
167     iterator insert( iterator itr, const Object & x )
168     {
169         Node *p = itr.current;
170         theSize++;
171         return iterator( p->prev = p->prev->next = new Node( x, p->prev, p ) );
172     }
173 
174     // Erase item at itr.
175     iterator erase( iterator itr )
176     {
177         Node *p = itr.current;
178         iterator retVal( p->next );
179         p->prev->next = p->next;
180         p->next->prev = p->prev;
181         delete p;
182         theSize--;
183  
184         return retVal;
185     }
186  
187     iterator erase( iterator start, iterator end )
188     {
189         for( iterator itr = from; itr != to; )
190             itr = erase( itr );
191  
192         return to;
193     }
194 
195 
196   private:
197     int   theSize;
198     Node *head;
199     Node *tail;
200 
201     void init( )
202     {
203         theSize = 0;
204         head = new Node;
205         tail = new Node;
206         head->next = tail;
207         tail->prev = head;
208     }
209 };

    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行的代码是必须的.

posted on 2011-11-02 22:24  Barryhe  阅读(4015)  评论(1编辑  收藏  举报

导航