本节目标:

添加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 }

结果如下:

 

问题已然解决了。

 

小结:

 

posted on 2018-09-15 10:11  周伯通789  阅读(342)  评论(0编辑  收藏  举报