在这一章主要学习STL的向量和表的实现。
3.3 STL中的向量和表
表ADT有两个流行的实现。vector给出了表ADT的可增长的数组实现。使用vector的有点在于其常量时间的索引。缺点就是插入新项或删除已有项代价昂贵,除了在末端。
list提供了表ADT的双向链表实现。使用list的有点是,如果变化发生的位置已知的话,插入新项和删除已有项的代价是很小的,缺点是list不容易索引。
常用的公共方法:
int size() const :返回容器内的元素的个数。
void clear():删除容器中所有元素。
bool empty():如果容器没有元素,返回TRUE,否则返回 FALSE
void push_back(const Object & x):在表末端添加元素。
void pop_back():在表末端弹出元素。
const Object &back() const:返回末端元素。
const Object &front() const:返回前端元素。
void push_front(const Object &x) 返回在list前端添加元素。
void pop_front() 在list前端删除元素
Object & operator[] (int idx):返回vector的索引值
Object & at(int idx):带边界检测的索引对象值。
int capacity() const :返回vector内部的容量。
void reserve(int newCapacity) 设定vector新的容量。
常用迭代器方法:
iterator begin() 容器第一项。
iterator end() 容器终止
itr++和++itr: 推进迭代器itr 至下一个位置。
*itr 返回存储在迭代器itr指定位置的对象的引用。
itr1==itr2、 itr1!=itr2、
需要迭代器的容器操作:
iterator insert(iterator pos,const Object &x):添加x到表中迭代器pos 所指向的位置之前的位置。返回值是一个指向插入项位置的迭代器。
iterator erase(iterator pos):删除迭代器给出位置的对象。返回调用之前pos指向元素的下一个元素的位置。
iterator erase(iterator start,iterator end):删除所有的从位置start 开始,直到位置end的所有元素。
3.4 向量的实现
vector 是C++的基本数组。vector可以复制并且占用的内存可以自动回收。C++数组的一些重要特性。
1、数组就是指向一块内存的指针变量,实际的数组大小必须由程序员单独确定。
2、内存卡可以通过new[]来分配,但是相应也就用delete[]来释放。
3、内存块大小不能改变(可以重新分配大块空间,对原来释放)。
vector的一些实现巧妙的地方后面具体分析,我们的vector的实现直接上代码吧:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
//ADT 常见操作:add remove size contains union find /* Vector 常用功能: 1、Vector将仍然是基本数组(通过一个指针变量来指向分配的内存卡),数组的容量和当前数组项数目存储在Vector里。 2、Vector 将通过实现“三大函数”,为复制构造函数和operator=提供深复制,同时也提供机构函数来回收基本数组。 3、Vector提供resize改变Vector大小,reverse来改变容量。分配一块新的内存然后复制旧内存,再然后释放内存。 4、Vector 将提供operator[]实现 5、Vector将提供size empty clear back pop_back push_back 6、Vector将支持嵌套的iterator和const_iterator和begin() 和end() 方法。 */ template < typename Object> class Vector { public: //构造函数 explicit Vector( int initSize = 0): theSize(initSize), theCapacity(initSize + SPARE_CAPACITY) { objects = new Object[theCapacity]; } //复制构造函数与operator =相同 Vector( const Vector &rhs): objects( NULL) { operator=(rhs); } //析构函数 ~Vector() { delete []objects; } //operator = 函数 const Vector & operator=( const Vector &rhs) { if( this != rhs) { delete []objects; theSize = rhs.theSize; theCapacity = rhs.theCapacity; objects = new Object[capacity()]; for( int i = 0; k < size(); k++) { objects[i] = rhs.object[i]; } } return * this; } //容量函数 int capacity() const { return theCapacity; } //大小函数 int size() const { return theSize; } //扩大小函数 void resize( int newSize) { if(newSize > theCapacity) reserve(newSize * 2 + 1); theSize = newSize; } //扩容函数 void reserve( int newSize) { if(newSize < theSize) return ; Object *oldArray = objects; objects = new Object[newSize]; for( int i = 0; i < theSize; i++) { objects[i] = oldArray[i]; } theCapacity = newSize; delete []oldArray; } //索引操作 Object & operator[]( int index) { return objects[index]; } const Object & operator[]( int index) const { return objects[index]; } //空判断 bool empty() const { return size() == 0; } //在末端插入 void push_back( const Object &x) { if(theSize == theCapacity) reserve( 2 * theCapacity + 1); objects[theSize++] = x; } //在末端删除 void pop_back() { theSize--; } //获取末端元素 const Object &back() const { return objects[theSize - 1]; } //定义iterator //定义const_iterator typedef Object *iterator; typedef Object *const_iterator; //开始 iterator begin() { return &objects[ 0]; } //结束 iterator end() { return &objects[theSize - 1]; } //常量开始 const_iterator begin() const { return &objects[ 0]; } //常量结束 const_iterator end() const { return &objects[theSize - 1]; } //使用enum来实现宏定义,effetive C++ enum { SPARE_CAPACITY = 10}; private: int theSize; //大小 int theCapacity; //容量 Object *objects; //数组 }; |
常规函数的实现:构造函数、析构函数、复制构造函数、operator=函数实现。其中构造函数和析构函数通过new[]和delete[]函数, 复制构造函数通过operator=函数实现。
其中operator=函数,首先通过if(this!=rhs)来判断是否自身,删除旧空间,复制大小,容量,新建空间、用for循环复制。返回*this。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//构造函数 explicit Vector( int initSize = 0): theSize(initSize), theCapacity(initSize + SPARE_CAPACITY) { objects = new Object[theCapacity]; } //复制构造函数与operator =相同 Vector( const Vector &rhs): objects( NULL) { operator=(rhs); } //析构函数 ~Vector() { delete []objects; } //operator = 函数 const Vector & operator=( const Vector &rhs) { if( this != rhs) { delete []objects; theSize = rhs.theSize; theCapacity = rhs.theCapacity; objects = new Object[capacity()]; for( int i = 0; k < size(); k++) { objects[i] = rhs.object[i]; } } return * this; } |
容量常见操作:扩容函数,需要重新分配一个新的空间,然后复制旧空间的数据到新空间的数据,重定义容量大小,再删除旧的空间。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//扩充大小函数 void resize( int newSize) { if(newSize > theCapacity) reserve(newSize * 2 + 1); theSize = newSize; } //扩充容量大小函数 void reserve( int newSize) { if(newSize < theSize) return ; Object *oldArray = objects; objects = new Object[newSize]; for( int i = 0; i < theSize; i++) { objects[i] = oldArray[i]; } theCapacity = newSize; delete []oldArray; } |
1
|
enum { SPARE_CAPACITY =
10};
|
3.5 表的实现
表的实现采用双向链表实现。我们直接上代码吧,后面具体分析实现的一些巧妙的地方。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
//表的实现 /* List 类是双向链表 (1) List 类本身。包含连接到表两端的链接,表的大小以及一系列方法。 (2) Node类。该类看起来像是私有的嵌套类。一个节点包含数据和用来指向其前和其后的节点的指针,以及适当的构造函数。 (3) const_iterator类。该类抽象了位置的概念,是一个公有的嵌套类。除了operator*操作返回所指向项的引用,而不是该项的常量引用的功能外, iterator具有和const_iterator相同功能。重载操作符= == != 和++。 (4) iterator类。该类抽象了位置的概念,是一个公有的嵌套类。 */ template < typename Object> class List { private: struct Node { Node *prev; Node *next; Object data; 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++() //这个表示++i { 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( const List &rhs) { init(); * this = rhs; } ~List() { clear(); delete head; delete tail; } const List & operator=( const List &rhs) { if( this == &rhs) return * this; clear(); for(const_iterator itr = rhs.begin(); itr != rhs.end(); ++it) { 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); } int size() const { return theSize; } bool empty() const { return size() == 0; } 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(), x); } void pop_back() { erase(--end()); } iterator insert(iterator itr, const Object &x) { Node *p = itr.current; theSize++; return iterator(p->prv = p->prev->next = new Node(x, p->prev, p)); } 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 itr, iterator end) { for(iterator = itr; itr != end;) { itr = erase(itr); } return itr; } private: int theSize; Node *head; Node *tail; void init() { theSize = 0; head = new Node; tail = new Node; head->next = tail; tail->prev = head; } }; |
下面我们来分析一下list实现的一些巧妙的地方:
首先要实现const_iterator 这个内部类。其中特别要注意的是itr++和++itr的实现,需要对不同形式分别编写例程。它们拥有相同的名字,因此必须用不通的符号来区分。C++需要通过给前缀形式指定空参数表,给后缀形式指定一个匿名的int参数来赋予前缀和后缀形式以不同的标识。然后++itr调用零参数的operator++,而itr++调用单参数operator++。这个int参数永远也不使用,其存在的意义仅仅在于给出一个不同的标识。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public:
class const_iterator { public: const_iterator(): current( NULL) { } const Object & operator*() const // { return retrieve(); } const_iterator &operator++() //这个表示++i { current = current->next; return *this; } const_iterator &operator++(int) //这个表示i++ { 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>; }; |
下图是构造函数和三大函数,因为零参数构造函数和复制函数两者都必须分配表头节点和尾节点,我们给出了一个私有的init例程,init生成一个空List。析构函数回收表头节点和尾节点,所有的其他节点在析构调用clear时回收,相似的,operator=是通过调用公有方法实现,而没有试图使用低级指针操作。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
List()
//构造函数 { init(); } List( const List &rhs) //复制构造函数 { init(); * this = rhs; //调用=实现。 } ~List() //析构函数 { clear(); delete head; delete tail; } const List & operator=( const List &rhs) //operator函数 { if( this == &rhs) return * this; clear(); for(const_iterator itr = rhs.begin(); itr != rhs.end(); ++it) { push_back(*itr); } return * this; } void init() //初始化 { theSize = 0; head = new Node; tail = new Node; head->next = tail; tail->prev = head; } |
最后的插入,删除也是一般的链表操作,在这里我们就不仔细讲解了。
栈和队列在STL采用stack和queue。