本节目标:
添加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 Node m_header; 21 int m_length; 22 public: 23 LinkList() 24 { 25 m_header.next = NULL; 26 m_length = 0; 27 } 28 29 bool insert(const T& e) 30 { 31 return insert(m_length, e); 32 } 33 34 bool insert(int i, const T& e) 35 { 36 bool ret = ((0 <= i) && (i <= m_length)); 37 38 if( ret ) 39 { 40 Node* node = new Node(); 41 42 if( node != NULL ) 43 { 44 Node* current = &m_header; 45 46 for(int p=0; p<i; p++) 47 { 48 current = current->next; 49 } 50 51 node->value = e; 52 node->next = current->next; 53 current->next = node; 54 55 m_length++; 56 } 57 else 58 { 59 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 60 } 61 } 62 63 return ret; 64 } 65 66 bool remove(int i) 67 { 68 bool ret = ((0 <= i) && (i < m_length)); 69 70 if( ret ) 71 { 72 Node* current = &m_header; 73 74 for(int p=0; p<i; p++) 75 { 76 current = current->next; 77 } 78 79 Node* toDel = current->next; 80 81 current->next = toDel->next; 82 83 delete toDel; 84 85 m_length--; 86 } 87 88 return ret; 89 } 90 91 bool set(int i, const T& e) 92 { 93 bool ret = ((0 <= i) && (i < m_length)); 94 95 if( ret ) 96 { 97 Node* current = &m_header; 98 99 for(int p=0; p<i; p++) 100 { 101 current = current->next; 102 } 103 104 current->next->value = e; 105 } 106 107 return ret; 108 } 109 110 bool get(int i, T& e) const 111 { 112 bool ret = ((0 <= i) && (i < m_length)); 113 114 if( ret ) 115 { 116 Node* current = &m_header; 117 118 for(int p=0; p<i; p++) 119 { 120 current = current->next; 121 } 122 123 e = current->next->value; 124 } 125 126 return ret; 127 } 128 129 int length() const 130 { 131 return m_length; 132 } 133 134 void clear() 135 { 136 while( m_header.next ) 137 { 138 Node* toDel = m_header.next; 139 140 m_header.next = toDel->next; 141 142 delete toDel; 143 } 144 145 m_length = 0; 146 } 147 148 ~LinkList() 149 { 150 clear(); 151 } 152 }; 153 154 } 155 156 #endif // LINKLIST_H
第110行的get是const函数,116行我们取m_header的指针,这时编译器会认为我们要改变m_header,编译会报错,因此,我们在第20行给m_header这个变量加上mutable属性。
测试程序如下:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 9 int main() 10 { 11 12 LinkList<int> list; 13 14 for(int i = 0; i<5; i++) 15 { 16 list.insert(i); 17 } 18 19 for(int i = 0; i < list.length(); i++) 20 { 21 int v = 0; 22 23 list.get(i, v); 24 25 cout << v << endl; 26 } 27 return 0; 28 }
运行结果如下:
get函数的使用不是很方便,我们添加一个重载函数:
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 Node m_header; 21 int m_length; 22 public: 23 LinkList() 24 { 25 m_header.next = NULL; 26 m_length = 0; 27 } 28 29 bool insert(const T& e) 30 { 31 return insert(m_length, e); 32 } 33 34 bool insert(int i, const T& e) 35 { 36 bool ret = ((0 <= i) && (i <= m_length)); 37 38 if( ret ) 39 { 40 Node* node = new Node(); 41 42 if( node != NULL ) 43 { 44 Node* current = &m_header; 45 46 for(int p=0; p<i; p++) 47 { 48 current = current->next; 49 } 50 51 node->value = e; 52 node->next = current->next; 53 current->next = node; 54 55 m_length++; 56 } 57 else 58 { 59 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 60 } 61 } 62 63 return ret; 64 } 65 66 bool remove(int i) 67 { 68 bool ret = ((0 <= i) && (i < m_length)); 69 70 if( ret ) 71 { 72 Node* current = &m_header; 73 74 for(int p=0; p<i; p++) 75 { 76 current = current->next; 77 } 78 79 Node* toDel = current->next; 80 81 current->next = toDel->next; 82 83 delete toDel; 84 85 m_length--; 86 } 87 88 return ret; 89 } 90 91 bool set(int i, const T& e) 92 { 93 bool ret = ((0 <= i) && (i < m_length)); 94 95 if( ret ) 96 { 97 Node* current = &m_header; 98 99 for(int p=0; p<i; p++) 100 { 101 current = current->next; 102 } 103 104 current->next->value = e; 105 } 106 107 return ret; 108 } 109 110 T get(int i) const 111 { 112 T ret; 113 114 if( get(i, ret) ) 115 { 116 return ret; 117 } 118 else 119 { 120 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 121 } 122 123 return ret; 124 } 125 126 bool get(int i, T& e) const 127 { 128 bool ret = ((0 <= i) && (i < m_length)); 129 130 if( ret ) 131 { 132 Node* current = &m_header; 133 134 for(int p=0; p<i; p++) 135 { 136 current = current->next; 137 } 138 139 e = current->next->value; 140 } 141 142 return ret; 143 } 144 145 int length() const 146 { 147 return m_length; 148 } 149 150 void clear() 151 { 152 while( m_header.next ) 153 { 154 Node* toDel = m_header.next; 155 156 m_header.next = toDel->next; 157 158 delete toDel; 159 } 160 161 m_length = 0; 162 } 163 164 ~LinkList() 165 { 166 clear(); 167 } 168 }; 169 170 } 171 172 #endif // LINKLIST_H
第110行为添加的重载的get函数,这个函数直接返回需要的值。
测试程序如下:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 9 int main() 10 { 11 12 LinkList<int> list; 13 14 for(int i = 0; i<5; i++) 15 { 16 list.insert(i); 17 } 18 19 for(int i = 0; i < list.length(); i++) 20 { 21 cout << list.get(i) << endl; 22 } 23 24 list.remove(2); 25 26 for(int i = 0; i < list.length(); i++) 27 { 28 cout << list.get(i) << endl; 29 } 30 31 return 0; 32 }
结果如下:
问题:
头结点是否存在隐患?实现代码是否需要优化?
我们定义的Node结构里面,只用到了next成员,而value成员是由用户指定的,如果用户自己定义了一个类型Test,在这个类型的构造函数中抛出异常。 如果用户使用这个Test类型来定义list的话,就会出现问题。
测试程序:
结果如下:
我们根本没有创建有问题的类Test的对象,只是创建了一个单链表LinkList的对象,而这确报错了。
没有创建Test类对象,就报错了,这时库的创建者是要负责的,而不是用户来负责。
原因:
我们的LinkList类中有一个m_header成员,在构造LinkList对象的时候,也会构造Node对象,进一步构造T对象,如下:
构造T对象时就和用户定义的对象类型有关了。
改进程序:
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 21 { 22 char reserved[sizeof(T)]; 23 Node* next; 24 }m_header; 25 26 int m_length; 27 public: 28 LinkList() 29 { 30 m_header.next = NULL; 31 m_length = 0; 32 } 33 34 bool insert(const T& e) 35 { 36 return insert(m_length, e); 37 } 38 39 bool insert(int i, const T& e) 40 { 41 bool ret = ((0 <= i) && (i <= m_length)); 42 43 if( ret ) 44 { 45 Node* node = new Node(); 46 47 if( node != NULL ) 48 { 49 Node* current = reinterpret_cast<Node*>(&m_header); 50 51 for(int p=0; p<i; p++) 52 { 53 current = current->next; 54 } 55 56 node->value = e; 57 node->next = current->next; 58 current->next = node; 59 60 m_length++; 61 } 62 else 63 { 64 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 65 } 66 } 67 68 return ret; 69 } 70 71 bool remove(int i) 72 { 73 bool ret = ((0 <= i) && (i < m_length)); 74 75 if( ret ) 76 { 77 Node* current = reinterpret_cast<Node*>(&m_header); 78 79 for(int p=0; p<i; p++) 80 { 81 current = current->next; 82 } 83 84 Node* toDel = current->next; 85 86 current->next = toDel->next; 87 88 delete toDel; 89 90 m_length--; 91 } 92 93 return ret; 94 } 95 96 bool set(int i, const T& e) 97 { 98 bool ret = ((0 <= i) && (i < m_length)); 99 100 if( ret ) 101 { 102 Node* current = reinterpret_cast<Node*>(&m_header); 103 104 for(int p=0; p<i; p++) 105 { 106 current = current->next; 107 } 108 109 current->next->value = e; 110 } 111 112 return ret; 113 } 114 115 T get(int i) const 116 { 117 T ret; 118 119 if( get(i, ret) ) 120 { 121 return ret; 122 } 123 else 124 { 125 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 126 } 127 128 return ret; 129 } 130 131 bool get(int i, T& e) const 132 { 133 bool ret = ((0 <= i) && (i < m_length)); 134 135 if( ret ) 136 { 137 Node* current = reinterpret_cast<Node*>(&m_header); 138 139 for(int p=0; p<i; p++) 140 { 141 current = current->next; 142 } 143 144 e = current->next->value; 145 } 146 147 return ret; 148 } 149 150 int length() const 151 { 152 return m_length; 153 } 154 155 void clear() 156 { 157 while( m_header.next ) 158 { 159 Node* toDel = m_header.next; 160 161 m_header.next = toDel->next; 162 163 delete toDel; 164 } 165 166 m_length = 0; 167 } 168 169 ~LinkList() 170 { 171 clear(); 172 } 173 }; 174 175 } 176 177 #endif // LINKLIST_H
我们将m_header定义成了匿名类型,在内存布局中m_header和Node是一样的,这时构造LinkList对象时就不会调用T类型的构造函数了。
此外,还需要改动49、77、102、137行,需要进行类型转换,否则编译不过。使用的类型转换为内存重解释reinterpret_cast<Node*>(&m_header)。
测试程序:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 class Test 9 { 10 public: 11 Test() 12 { 13 throw 0; 14 } 15 }; 16 17 int main() 18 { 19 20 LinkList<Test> list; 21 22 cout << "Hello World" << endl; 23 24 return 0; 25 }
这时就正常了,结果如下:
代码优化:
我们的程序中多次使用了current定位,因此可以进行重构:
改进程序:
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 21 { 22 char reserved[sizeof(T)]; 23 Node* next; 24 }m_header; 25 26 int m_length; 27 28 Node* position(int i) const 29 { 30 Node* ret = reinterpret_cast<Node*>(&m_header); 31 32 for(int p = 0; p < i; p++) 33 { 34 ret = ret->next; 35 } 36 37 return ret; 38 } 39 public: 40 LinkList() 41 { 42 m_header.next = NULL; 43 m_length = 0; 44 } 45 46 bool insert(const T& e) 47 { 48 return insert(m_length, e); 49 } 50 51 bool insert(int i, const T& e) 52 { 53 bool ret = ((0 <= i) && (i <= m_length)); 54 55 if( ret ) 56 { 57 Node* node = new Node(); 58 59 if( node != NULL ) 60 { 61 Node* current = position(i); 62 63 node->value = e; 64 node->next = current->next; 65 current->next = node; 66 67 m_length++; 68 } 69 else 70 { 71 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 72 } 73 } 74 75 return ret; 76 } 77 78 bool remove(int i) 79 { 80 bool ret = ((0 <= i) && (i < m_length)); 81 82 if( ret ) 83 { 84 Node* current = position(i); 85 86 Node* toDel = current->next; 87 88 current->next = toDel->next; 89 90 delete toDel; 91 92 m_length--; 93 } 94 95 return ret; 96 } 97 98 bool set(int i, const T& e) 99 { 100 bool ret = ((0 <= i) && (i < m_length)); 101 102 if( ret ) 103 { 104 position(i)->next->value = e; 105 } 106 107 return ret; 108 } 109 110 T get(int i) const 111 { 112 T ret; 113 114 if( get(i, ret) ) 115 { 116 return ret; 117 } 118 else 119 { 120 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 121 } 122 123 return ret; 124 } 125 126 bool get(int i, T& e) const 127 { 128 bool ret = ((0 <= i) && (i < m_length)); 129 130 if( ret ) 131 { 132 e = position(i)->next->value; 133 } 134 135 return ret; 136 } 137 138 int length() const 139 { 140 return m_length; 141 } 142 143 void clear() 144 { 145 while( m_header.next ) 146 { 147 Node* toDel = m_header.next; 148 149 m_header.next = toDel->next; 150 151 delete toDel; 152 } 153 154 m_length = 0; 155 } 156 157 ~LinkList() 158 { 159 clear(); 160 } 161 }; 162 163 } 164 165 #endif // LINKLIST_H
我们添加了position函数用来定位。
测试程序:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 class Test 9 { 10 public: 11 Test() 12 { 13 throw 0; 14 } 15 }; 16 17 int main() 18 { 19 20 LinkList<Test> list; 21 22 cout << "Hello World" << endl; 23 24 Test t; 25 26 list.insert(t); 27 28 return 0; 29 }
结果如下:
可以看到是先打印的Hello World然后报的异常,这就是用户定义Test类对象的问题了,这应该由用户自己负责了。
再次运行测试程序:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 9 int main() 10 { 11 LinkList<int> list; 12 13 for(int i = 0; i<5; i++) 14 { 15 list.insert(0,i); 16 list.set(0, i * i); 17 } 18 19 for(int i = 0; i < list.length(); i++) 20 { 21 cout << list.get(i) << endl; 22 } 23 24 list.clear(); 25 26 for(int i=0; i<list.length(); i++) 27 { 28 cout << list.get(i) << endl; 29 } 30 31 return 0; 32 }
这时只打印出了16,这不是我们想要的,可见重构代码出现了问题。
这时因为我们的匿名类型变量m_header没有继承自Object,这样的话导致m_header和Node的内存布局有可能不一样,改进程序:
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 28 Node* position(int i) const 29 { 30 Node* ret = reinterpret_cast<Node*>(&m_header); 31 32 for(int p = 0; p < i; p++) 33 { 34 ret = ret->next; 35 } 36 37 return ret; 38 } 39 public: 40 LinkList() 41 { 42 m_header.next = NULL; 43 m_length = 0; 44 } 45 46 bool insert(const T& e) 47 { 48 return insert(m_length, e); 49 } 50 51 bool insert(int i, const T& e) 52 { 53 bool ret = ((0 <= i) && (i <= m_length)); 54 55 if( ret ) 56 { 57 Node* node = new Node(); 58 59 if( node != NULL ) 60 { 61 Node* current = position(i); 62 63 node->value = e; 64 node->next = current->next; 65 current->next = node; 66 67 m_length++; 68 } 69 else 70 { 71 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 72 } 73 } 74 75 return ret; 76 } 77 78 bool remove(int i) 79 { 80 bool ret = ((0 <= i) && (i < m_length)); 81 82 if( ret ) 83 { 84 Node* current = position(i); 85 86 Node* toDel = current->next; 87 88 current->next = toDel->next; 89 90 delete toDel; 91 92 m_length--; 93 } 94 95 return ret; 96 } 97 98 bool set(int i, const T& e) 99 { 100 bool ret = ((0 <= i) && (i < m_length)); 101 102 if( ret ) 103 { 104 position(i)->next->value = e; 105 } 106 107 return ret; 108 } 109 110 T get(int i) const 111 { 112 T ret; 113 114 if( get(i, ret) ) 115 { 116 return ret; 117 } 118 else 119 { 120 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 121 } 122 123 return ret; 124 } 125 126 bool get(int i, T& e) const 127 { 128 bool ret = ((0 <= i) && (i < m_length)); 129 130 if( ret ) 131 { 132 e = position(i)->next->value; 133 } 134 135 return ret; 136 } 137 138 int length() const 139 { 140 return m_length; 141 } 142 143 void clear() 144 { 145 while( m_header.next ) 146 { 147 Node* toDel = m_header.next; 148 149 m_header.next = toDel->next; 150 151 delete toDel; 152 } 153 154 m_length = 0; 155 } 156 157 ~LinkList() 158 { 159 clear(); 160 } 161 }; 162 163 } 164 165 #endif // LINKLIST_H
第20行使匿名类继承自Object,测试程序如下:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 9 int main() 10 { 11 LinkList<int> list; 12 13 for(int i = 0; i<5; i++) 14 { 15 list.insert(0,i); 16 list.set(0, i * i); 17 } 18 19 for(int i = 0; i < list.length(); i++) 20 { 21 cout << list.get(i) << endl; 22 } 23 24 list.clear(); 25 26 for(int i=0; i<list.length(); i++) 27 { 28 cout << list.get(i) << endl; 29 } 30 31 return 0; 32 }
结果如下:
问题已然解决了。
小结: