C++学习笔记(八)----表(list)的实现
接下来,我们根据《数据结构和算法分析 C++描述》中图3-12至3-20的代码,继续回顾类的有关知识.
代码如下:
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 if( this == &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行的代码是必须的.