问题1:
glibc中的strdup实现如下:
没有对参数s进行空指针判断。
我们的Exception.cpp中应做改进:
在第12行进行判断空指针操作。
问题2:
t1在析构时会抛出异常,我们在remove一个对象时,要保证链表还是合法的,也就是保证异常安全性。
如上图,我们期望打印的链表长度为2。
示例程序:
1 #include <iostream> 2 #include "LinkList.h" 3 4 using namespace std; 5 using namespace DTLib; 6 7 class Test : public Object 8 { 9 int m_id; 10 public: 11 Test(int id = 0) 12 { 13 m_id = id; 14 } 15 16 ~Test() 17 { 18 if( m_id == 1 ) 19 { 20 throw m_id; 21 } 22 } 23 }; 24 25 int main() 26 { 27 LinkList<Test> list; 28 Test t0(0), t1(1), t2(2); 29 30 try 31 { 32 list.insert(t0); 33 list.insert(t1); 34 list.insert(t2); 35 36 list.remove(1); 37 } 38 catch(int e) 39 { 40 cout << e << endl; 41 cout << list.length() << endl; 42 } 43 44 return 0; 45 }
结果如下:
程序直接崩溃了。
vc下的运行结果如下:
打印e的值为1,链表长度为3,意味着单链表的状态出问题了。
我们的remove函数没有考虑异常安全性。
修改LinkList.h:
1 #ifndef LINKLIST_H 2 #define LINKLIST_H 3 4 #include "List.h" 5 #include "Exception.h" 6 7 namespace DTLib 8 { 9 10 template < typename T > 11 class LinkList : public List<T> 12 { 13 protected: 14 struct Node : public Object 15 { 16 T value; 17 Node* next; 18 }; 19 20 mutable struct : public Object 21 { 22 char reserved[sizeof(T)]; 23 Node* next; 24 }m_header; 25 26 int m_length; 27 int m_step; 28 Node* m_current; 29 30 Node* position(int i) const // O(n) 31 { 32 Node* ret = reinterpret_cast<Node*>(&m_header); 33 34 for(int p = 0; p < i; p++) 35 { 36 ret = ret->next; 37 } 38 39 return ret; 40 } 41 42 virtual Node* create() 43 { 44 return new Node(); 45 } 46 47 virtual void destroy(Node* pn) 48 { 49 delete pn; 50 } 51 52 public: 53 LinkList() 54 { 55 m_header.next = NULL; 56 m_length = 0; 57 m_step = 1; 58 m_current = NULL; 59 } 60 61 bool insert(const T& e) 62 { 63 return insert(m_length, e); 64 } 65 66 bool insert(int i, const T& e) // O(n) 67 { 68 bool ret = ((0 <= i) && (i <= m_length)); 69 70 if( ret ) 71 { 72 Node* node = create(); 73 74 if( node != NULL ) 75 { 76 Node* current = position(i); 77 78 node->value = e; 79 node->next = current->next; 80 current->next = node; 81 82 m_length++; 83 } 84 else 85 { 86 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 87 } 88 } 89 90 return ret; 91 } 92 93 bool remove(int i) // O(n) 94 { 95 bool ret = ((0 <= i) && (i < m_length)); 96 97 if( ret ) 98 { 99 Node* current = position(i); 100 101 Node* toDel = current->next; 102 103 current->next = toDel->next; 104 105 m_length--; 106 107 destroy(toDel); 108 109 } 110 111 return ret; 112 } 113 114 bool set(int i, const T& e) // O(n) 115 { 116 bool ret = ((0 <= i) && (i < m_length)); 117 118 if( ret ) 119 { 120 position(i)->next->value = e; 121 } 122 123 return ret; 124 } 125 126 T get(int i) const 127 { 128 T ret; 129 130 if( get(i, ret) ) 131 { 132 return ret; 133 } 134 else 135 { 136 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 137 } 138 139 return ret; 140 } 141 142 bool get(int i, T& e) const // O(n) 143 { 144 bool ret = ((0 <= i) && (i < m_length)); 145 146 if( ret ) 147 { 148 e = position(i)->next->value; 149 } 150 151 return ret; 152 } 153 154 int find(const T& e) const // O(n) 155 { 156 int ret = -1; 157 int i = 0; 158 159 Node* node = m_header.next; 160 161 while( node ) 162 { 163 if( node->value == e ) 164 { 165 ret = i; 166 break; 167 } 168 else 169 { 170 node = node->next; 171 i++; 172 } 173 } 174 175 return ret; 176 } 177 178 int length() const // O(1) 179 { 180 return m_length; 181 } 182 183 void clear() // O(n) 184 { 185 while( m_header.next ) 186 { 187 Node* toDel = m_header.next; 188 189 m_header.next = toDel->next; 190 191 m_length--; 192 193 destroy(toDel); 194 } 195 196 // m_length = 0; 197 } 198 199 bool move(int i, int step = 1) 200 { 201 bool ret = (0 <= i) && (i < m_length) && (step > 0); 202 203 if( ret ) 204 { 205 m_current = position(i)->next; 206 m_step = step; 207 } 208 209 return ret; 210 } 211 212 bool end() 213 { 214 return (m_current == NULL); 215 } 216 217 T current() 218 { 219 if( !end() ) 220 { 221 return m_current->value; 222 } 223 else 224 { 225 THROW_EXCEPTION(InvalidOperationException, "No value at current position ..."); 226 } 227 } 228 229 bool next() //每次移动step步 230 { 231 int i = 0; 232 233 while((i < m_step) && !end()) 234 { 235 m_current = m_current->next; 236 i++; 237 } 238 239 return (i == m_step); 240 } 241 242 ~LinkList() // O(n) 243 { 244 clear(); 245 } 246 }; 247 248 } 249 250 #endif // LINKLIST_H
在remove函数中,先让m_length--,再做摧毁节点的操作。
在clear函数中,注释掉原来的196行,添加第191行,每次摧毁前让m_length--。