本程序无法实现的功能就是想要头结点的数据域和其他结点的数据域类型不同,由于使用模板如果在构造函数的时候设置头结点的数据域类型,那么实例化的时候(LinkList<int>link;)其他结点数据域也要跟着变成整型,如果将头结点在构造函数中提前定义好(ListNode<int>*L=new ListNode<int>;),那么在类中封装的方法中难免会遇到头结点发生拷贝(ListNode<DataType>*temp=L;)则会因为DataType和int类型不匹配而发生错误(static_cast<ListNode<DataType>*>L也不起作用)

LinkList.hpp文件:

  1 #pragma once
  2 #include<iostream>
  3 #include<stdexcept>
  4 using std::cout;
  5 using std::endl;
  6 using std::cin;
  7 using std::string;
  8 /*
  9 * DataType   数据域的类型
 10 */
 11 
 12 template<class DataType>
 13 struct ListNode
 14 {
 15     DataType data_;
 16     ListNode<DataType>* next_;
 17     ListNode() :next_(nullptr){}
 18 };
 19 
 20 /*
 21 * 这里面注意类中的DataType,AdressType和结构体中的DataType,AdressType无关,完全可以用其他字母,但为了将类和结构体统一所以写成一样的
 22 * 因为实现不知道数据域指针域到底是什么类型所以用模板
 23 */
 24 template<class DataType>
 25 class LinkList
 26 {
 27 public:
 28     LinkList();//初始化
 29     ~LinkList();//销毁链表,包括删除头结点和头指针
 30     bool Empty();//判断链表是否为空链表,也就是头结点的指针域是否是空指针
 31     void RemoveAll();//删除所有结点(除头指针,头结点)
 32     int LinkSize();//判断链表的长度(首元结点及以后有几个结点)
 33     DataType FindIndexData(int index);//查找链表中下标为index的元素的数据域
 34     ListNode<DataType>* FindIndexAddress(int index);//查找链表中下标为index的元素的指针域
 35     ListNode<DataType>* FindDataAddress(DataType data);//根据指定的数据,获取该数据所在的结点地址
 36     int FindDataIndex(DataType data);//根据指定的数据,获取该数据所在的结点索引值
 37     void Insert(int index, DataType data, ListNode<DataType>* newnode);//在索引值为index前插入结点,链表不能为空
 38     void DeleteNode(int index);//删除结点
 39     void AddNode_input(int num);//头插法创建新链表,手动输入数据
 40     void AddNode(ListNode<DataType>* newnode, DataType data);//头插法创建新链表,调用函数
 41     void PushBack_input(int num);//尾插法,手动输入数据
 42     void PushBack(ListNode<DataType>* newnode, DataType data);//尾插法,调用函数
 43 
 44     ListNode<DataType>* L;//头指针(根据定义头指针是指向链表头结点的指针,指向结点,因此头指针应该为结点类型)
 45     int linkList_size = 0;//链表的长度
 46 
 47 };
 48 
 49 /*
 50 * 默认构造函数
 51 * 初始化链表,开辟一块堆区内存作为头结点,让头指针指向头结点
 52 * L   头指针
 53 */
 54 template<class DataType>
 55 LinkList<DataType>::LinkList()
 56 {
 57     L = new ListNode<DataType>;//为头结点开辟堆区内存,并将头指针指向该堆区内存 
 58 }
 59 
 60 /*
 61 * 析构函数
 62 * 删除头结点,头指针,以及其他所有结点
 63 * 方法是:首先调用RemoveAll函数删除所有结点,然后删除头结点和头指针
 64 */
 65 template<class DataType>
 66 LinkList<DataType>::~LinkList()
 67 {
 68     if (L->next_==0)
 69     {
 70         delete L;
 71         L = nullptr;
 72         return;
 73     }
 74     RemoveAll();
 75     delete L;
 76     L = nullptr;
 77 }
 78 
 79 /*
 80 * 判断链表是否为空的函数
 81 */
 82 template<class DataType>
 83 bool LinkList<DataType>::Empty()
 84 {
 85     if (linkList_size == 0)
 86     {
 87         return true;
 88     }
 89     else
 90     {
 91          return false;
 92     }
 93 }
 94 
 95 /*
 96 * 删除除头结点头指针外的其他所有结点
 97 * 方法是:首先用L_temp记录首元结点
 98 * 
 99 * 然后将L_temp指向的首元结点的地址赋给temp
100 * 然后将下一个结点的地址赋给L_temp
101 * 并用temp去释放首元结点(当前结点),其他结点同理
102 * 
103 * temp  指向当前结点,作用是释放当前结点,
104 * L_temp  指向当前结点的下一个结点
105 */
106 template<class DataType>
107 void LinkList<DataType>::RemoveAll()
108 {
109 
110     ListNode<DataType>* L_temp =  L->next_;
111 
112     ListNode<DataType>* temp;
113     if (L->next_==nullptr)
114     {
115         cout << "The LinkList is empty" << endl;
116         return;
117     }
118     while (L_temp!=nullptr)
119     {
120         temp = L_temp;
121         L_temp = L_temp->next_;
122         delete temp;
123     }
124     temp = nullptr;
125     linkList_size = 0;//链表长度置为0
126     L->next_ = nullptr;
127 }
128 
129 /*
130 * 计算链表的长度
131 */
132 template<class DataType>
133 int LinkList<DataType>::LinkSize()
134 {
135     return linkList_size;
136 }
137 
138 /*
139 * 查找链表中下标为index的元素的数据域函数
140 * 注意此处返回值类型是DataType,一个未知的类型,当我进行不正当的操作的时候,比如索引越界,我们就应该终止这个函数调用
141 * 但是我没法像普通函数那样return-1;return;因为我根本就不知道返回值类型是什么,目前看来只有异常才能解决这个问题
142 * 查找链表中下标为index的元素的数据域函数
143 * 链表是顺序存储只能按顺序查找
144 * 判断num是不是我们要找的索引值(index)不是就让find指向下一个结点
145 * find  指向待判断是否是查找结点的结点
146 * num  记录find的当前指向结点的索引值
147 */
148 template<class DataType>
149 DataType LinkList<DataType>::FindIndexData(int index)
150 {
151     ListNode<DataType>* find = L->next_;
152     int num = 0;
153     try
154     {
155         if (L->next_ == nullptr)
156         {
157             throw - 1;
158         }
159         if ((index < 0) || (index > linkList_size - 1))
160         {
161             throw std::runtime_error("LinkList subscript out of range");
162         }
163     }
164     catch (std::runtime_error& err)
165     {
166         cout << "************************" << endl;
167         cout << "You meet one abnormal:" << endl;
168         cout << err.what() << endl;
169         cout << "The range of subscript is " << "[" << 0 << "," << linkList_size-1 << "]" << endl;
170         cout << "Please input the right index:" << endl;
171         int right_index;
172         cin >> right_index;
173         index = right_index;
174     }
175     catch (const int&)
176     {
177         cout << "************************" << endl;
178         cout << "You meet one abnormal:" << endl;
179         std::cerr << "This is a empty linklist" << endl;
180         std::terminate();
181     }
182     while(num!=index)
183     {
184         ++num;
185         find = find->next_;
186     }
187     return find->data_;
188 }
189 
190 /*
191 * 查找链表中下标为index的元素的指针域函数(从首元结点查起)
192 */
193 template<class DataType>
194 ListNode<DataType>* LinkList<DataType>::FindIndexAddress(int index)
195 {
196     ListNode<DataType>* find = L->next_;
197     int num = 0;
198     try
199     {
200         if (L->next_==nullptr)
201         {
202             throw - 1;
203         }
204         if ((index < 0) || (index > linkList_size - 1))
205         {
206             throw std::runtime_error("LinkList subscript out of range");
207         }
208     }
209     catch (std::runtime_error& err)
210     {
211         cout << "************************" << endl;
212         cout << "You meet one abnormal:" << endl;
213         cout << err.what() << endl;
214         cout << "The range of subscript is " << "[" << 0 << "," << linkList_size-1 << "]" << endl;
215         cout << "Please input the right index:" << endl;
216         int right_index;
217         cin >> right_index;
218         index = right_index;
219     }
220     catch (const int&)
221     {
222         cout << "************************" << endl;
223         cout << "You meet one abnormal:" << endl;
224         std::cerr << "This is a empty linklist" << endl;
225         std::terminate();
226     }
227     while (num != index)
228     {
229         ++num;
230         find = find->next_;
231     }
232     return find;
233 }
234 
235 /*
236 * 根据指定的数据,获取该数据所在的结点地址函数,没有找到则返回空
237 */
238 template<class DataType>
239 ListNode<DataType>* LinkList<DataType>::FindDataAddress(DataType data)
240 {
241     ListNode<DataType>* find = L->next_;
242     while ((find != nullptr)&&(find->data_!=data))
243     {
244         find = find->next_;
245     }
246     return find;
247 }
248 
249 /*
250 * 根据指定的数据,获取该数据所在的结点索引值函数,没有就返回-1
251 * index  用来记录find所指结点的索引值
252 */
253 template<class DataType>
254 int LinkList<DataType>::FindDataIndex(DataType data)
255 {
256     int index=0;
257     ListNode<DataType>* find = L->next_;
258     while (find != nullptr)
259     {
260         if (find->data_ == data)
261         {
262             return index;
263         }
264         ++index;
265         find = find->next_;
266     }
267     return -1;
268 }
269 
270 /*
271 * 在索引值为index前插入结点函数,支持尾插,链表不能为空
272 * 方法是:将index-1结点的指针域赋给newnode的指针域,然后将newnode的地址赋给index-1位置结点的指针域
273 * 也就是说想要在index插入实际上操作的是n-1
274 */
275 template<class DataType>
276 void LinkList<DataType>::Insert(int index, DataType data, ListNode<DataType>* newnode)
277 {
278     ListNode<DataType>* find = L;
279     int num = -1;
280     try
281     {
282         if (L->next_ == nullptr)
283         {
284             throw - 1;
285         }
286         if ((index < 0) || (index > linkList_size - 1))
287         {
288             throw std::runtime_error("LinkList subscript out of range");
289         }
290     }
291     catch (std::runtime_error& err)
292     {
293         cout << "************************" << endl;
294         cout << "You meet one abnormal:" << endl;
295         cout << err.what() << endl;
296         cout << "The range of subscript is " << "[" << 0 << "," << linkList_size-1 << "]" << endl;
297         cout << "Please input the right index:" << endl;
298         int right_index;
299         cin >> right_index;
300         index = right_index;
301     }
302     catch (const int&)
303     {
304         cout << "************************" << endl;
305         cout << "You meet one abnormal:" << endl;
306         std::cerr << "This is a empty linklist" << endl;
307         std::terminate();
308     }
309     while (num != (index-1))
310     {
311         ++num;
312         find = find->next_;
313     }
314     newnode->next_ = find->next_;
315     find->next_ = newnode;
316     newnode->data_ = data;
317     ++linkList_size;
318 }
319 
320 /*
321 * 删除当前结点
322 * 方法是:将待删除结点的指针域赋给上个结点的指针域
323 */
324 /*
325 * 在索引值为index前插入结点函数,支持尾插
326 * 方法是:将index-1结点的指针域赋给newnode的指针域,然后将newnode的地址赋给index-1位置结点的指针域
327 * 也就是说想要在index插入实际上操作的是n-1
328 */
329 template<class DataType>
330 void LinkList<DataType>::DeleteNode(int index)
331 {
332     ListNode<DataType>* temp;
333     ListNode<DataType>* find = L;
334     int num = -1;
335     try
336     {
337         if (L->next_ == nullptr)
338         {
339             throw - 1;
340         }
341         if ((index < 0) || (index > linkList_size - 1))
342         {
343             throw std::runtime_error("LinkList subscript out of range");
344         }
345     }
346     catch (std::runtime_error& err)
347     {
348         cout << "************************" << endl;
349         cout << "You meet one abnormal:" << endl;
350         cout << err.what() << endl;
351         cout << "The range of subscript is " << "[" << 0 << "," << linkList_size-1 << "]" << endl;
352         cout << "Please input the right index:" << endl;
353         int right_index;
354         cin >> right_index;
355         index = right_index;
356     }
357     catch (const int&)
358     {
359         cout << "************************" << endl;
360         cout << "You meet one abnormal:" << endl;
361         std::cerr << "This is a empty linklist" << endl;
362         std::terminate();
363     }
364     while (num != (index - 1))
365     {
366         ++num;
367         find = find->next_;
368     }
369     temp = find->next_;
370     find->next_ = find->next_->next_;
371     delete temp;
372     temp = nullptr;
373     --linkList_size;
374 }
375 /*
376 * 头插法创建新链表,手动输入数据
377 * 创建一个a->b->c的链表,首先将c接入链表末尾,然后将b接入头结点和c之间,然后将a接入
378 */
379 
380 template<class DataType>
381 void LinkList<DataType>::AddNode_input(int num)
382 {
383     cout << "Please creat " << num << " node" << endl;
384     for (int i = 0; i < num; ++i)
385     {
386         ListNode<DataType>* newnode = new ListNode<DataType>;
387         newnode->next_ = L->next_;
388         cout << "Please enter " << num-i << " more pieces of data " << endl;
389         cin >> newnode->data_;
390         L->next_ = newnode;
391         ++linkList_size;
392     }
393 }
394 /*
395 * 头插法创建新链表,调用函数
396 * 创建一个a->b->c的链表,首先将c接入链表末尾,然后将b接入头结点和c之间,然后将a接入
397 */
398 
399 template<class DataType>
400 void LinkList<DataType>::AddNode(ListNode<DataType>*newnode,DataType data)
401 {
402     newnode->next_ = L->next_;
403     newnode->data_ = data;
404     L->next_ = newnode;
405     ++linkList_size;
406 }
407 
408 /*
409 * 尾插法
410 * 方法是:将新结点的地址赋给temp的指针域,接着将temp指向该新结点,以便于后续结点插入
411 * temp 指向新结点的指针
412 */
413 template<class DataType>
414 void LinkList<DataType>::PushBack_input(int num)
415 {
416     cout << "Please creat " << num << " node" << endl;
417     ListNode<DataType>* temp = L;
418     for (int i = 0; i < num; ++i)
419     {
420         ListNode<DataType>* newnode = new ListNode<DataType>;
421         temp->next_ = newnode;
422         temp = newnode;
423         cout << "Please enter " << num - i << " more pieces of data " << endl;
424         cin >> temp->data_;
425         ++linkList_size;
426     }
427 }
428 
429 /*
430 * 尾插法
431 * 方法是:将新结点的地址赋给temp的指针域,接着将temp指向该新结点,以便于后续结点插入
432 * temp 指向新结点的指针
433 */
434 template<class DataType>
435 void LinkList<DataType>::PushBack(ListNode<DataType>* newnode, DataType data)
436 {
437     //static使下次调用的时候temp依然指向上一个新的结点,不会刷新
438     //如果一个函数的第一次调用和其他调用不一样的话往往需要static
439     static ListNode<DataType>* temp = L;
440     temp->next_ = newnode;
441     temp = newnode;
442     temp->data_ = data;
443     ++linkList_size;
444 }
  1 #include<iostream>
  2 #include"LinkList.hpp"
  3 using std::endl;
  4 using std::cout;
  5 using std::string;
  6 /*
  7 * 如果类中有运算符,那么只需要在主文件中重载这些运算符就可以使用这个类了
  8 */
  9 struct student
 10 {
 11     int age = 0;
 12     string name = "";
 13 };
 14 std::ostream& operator<<(std::ostream& cout, const student& st) {
 15     cout << st.age<< endl;
 16     cout << st.name << endl;
 17     return cout;
 18 }
 19 bool operator==(const student& st1, const student& st2) {
 20     if ((st1.age == st2.age) && (st1.name == st2.name))
 21     {
 22         return true;
 23     }
 24     else
 25     {
 26         return false;
 27     }
 28 }
 29 bool operator!=(const student& st1, const student& st2) {
 30     if ((st1.age == st2.age) && (st1.name == st2.name))
 31     {
 32         return false;
 33     }
 34     else 
 35     {
 36         return true;
 37     }
 38 }
 39 int main()
 40 {
 41     student stu1 = { 80,"张三" };
 42     student stu2 = { 90,"李四" };
 43     student stu3 = { 100,"王五" };
 44     ListNode<student>* node1=new ListNode<student>;
 45     ListNode<student>* node2 = new ListNode<student>;
 46     ListNode<student>* node3 = new ListNode<student>;
 47     LinkList<student>link_student;
 48     link_student.PushBack(node1, stu1);
 49     link_student.PushBack(node2, stu2);
 50     link_student.PushBack(node3, stu3);
 51 
 52     int val1 = 20;
 53     int val2 = 30;
 54     int val3 = 40;
 55     ListNode<int>* node4=new ListNode<int>;
 56     ListNode<int>* node5 = new ListNode<int>;
 57     ListNode<int>* node6 = new ListNode<int>;
 58     LinkList<int>link_int;
 59     link_int.PushBack(node4, val1);
 60     link_int.PushBack(node5, val2);
 61     link_int.PushBack(node6, val3);
 62 
 63     //注意自定义类型无法通过循环加cin填加数据
 64     cout << "stduent的测试————————————" << endl;
 65     cout << link_student.Empty() << endl;
 66     cout << "————————————————" << endl;
 67     cout << link_student.FindDataAddress(stu2) << endl;
 68     cout << "————————————————" << endl;
 69     cout << link_student.FindIndexAddress(1) << endl;
 70     cout << "————————————————" << endl;
 71     cout << link_student.FindDataIndex(stu2) << endl;
 72     cout << "————————————————" << endl;
 73     cout << link_student.FindIndexData(1) << endl;
 74     cout << "————————————————" << endl;
 75     link_student.DeleteNode(1);
 76     cout << link_student.FindIndexData(1) << endl;
 77     cout << "————————————————" << endl;
 78     //错误1:必须重新建立结点,因为原来的node2堆区已经被释放
 79     //link_student.Insert(1, stu2, node2);
 80     //cout << link_student.FindIndexData(1) << endl;
 81     ListNode<student>* node2_2 = new ListNode<student>;
 82     student stu2_2 = { 60,"赵六" };
 83     link_student.Insert(1, stu2_2, node2_2);
 84     cout << link_student.FindIndexData(1) << endl;
 85     cout << "————————————————" << endl;
 86     link_student.RemoveAll();
 87     cout << link_student.LinkSize() << endl;
 88     cout << link_student.linkList_size << endl;
 89     cout << "————————————————" << endl;
 90     //注意这样使用的话是每次在首元结点之前插入数据
 91     ListNode<student>* node7 = new ListNode<student>;
 92     student stu4 = { 120,"钱七" };
 93     link_student.AddNode(node7, stu4);
 94     cout << link_student.FindIndexData(0) << endl;
 95 
 96     cout << "int的测试————————————" << endl;
 97     cout << link_int.Empty() << endl;
 98     cout << "————————————————" << endl;
 99     cout << link_int.FindDataAddress(val2) << endl;
100     cout << "————————————————" << endl;
101     cout << link_int.FindIndexAddress(1) << endl;
102     cout << "————————————————" << endl;
103     cout << link_int.FindDataIndex(val2) << endl;
104     cout << "————————————————" << endl;
105     cout << link_int.FindIndexData(1) << endl;
106     cout << "————————————————" << endl;
107     link_int.DeleteNode(1);
108     cout << link_int.FindIndexData(1) << endl;
109     cout << "————————————————" << endl;
110     ListNode<int>* node5_2 = new ListNode<int>;
111     int val5_2 = 50;
112     link_int.Insert(1, val5_2, node5_2);
113     cout << link_int.FindIndexData(1) << endl;
114     cout << "————————————————" << endl;
115     link_int.RemoveAll();
116     cout << link_int.LinkSize() << endl;
117     cout << link_int.linkList_size << endl;
118     cout << "————————————————" << endl;
119     //注意这样使用的话是每次在首元结点之前插入数据
120     ListNode<int>* node8 = new ListNode<int>;
121     int val7 = 60;
122     link_int.AddNode(node8, val7);
123     cout << link_int.FindIndexData(0) << endl;
124     cout << "————————————————" << endl;
125     link_int.AddNode_input(3);
126     cout << "————————————————" << endl;
127     cout << link_int.FindIndexData(0) << endl;
128     cout << link_int.FindIndexData(1) << endl;
129     cout << link_int.FindIndexData(2) << endl;
130     //尾插和头插不能一起用
131     //link_int.PushBack_input(3);
132     //cout << link_int.linkList_size << endl;
133     ////错误2:之所以不会抛出前面所写的异常是因为判断语句中用的linklist_size,导致不会执行判断语句
134     //cout << link_int.FindIndexData(4) << endl;
135     system("pause");
136     return 0;
137 }
 1 stduent的测试————————————
 2 0
 3 ————————————————
 4 000001DFF6AF5DF0
 5 ————————————————
 6 000001DFF6AF5DF0
 7 ————————————————
 8 1
 9 ————————————————
10 90
11 李四
12 
13 ————————————————
14 100
15 王五
16 
17 ————————————————
18 60
19 赵六
20 
21 ————————————————
22 0
23 0
24 ————————————————
25 120
26 钱七
27 
28 int的测试————————————
29 0
30 ————————————————
31 000001DFF6B03650
32 ————————————————
33 000001DFF6B03650
34 ————————————————
35 1
36 ————————————————
37 30
38 ————————————————
39 40
40 ————————————————
41 50
42 ————————————————
43 0
44 0
45 ————————————————
46 60
47 ————————————————
48 Please creat 3 node
49 Please enter 3 more pieces of data
50 120
51 Please enter 2 more pieces of data
52 110
53 Please enter 1 more pieces of data
54 100
55 ————————————————
56 100
57 110
58 120

错误1:

使用已经释放的堆区

错误2:(读取访问权限冲突)

往往是下标越界造成的

以上错误都是调试才能看出来

posted on 2023-06-21 19:46  小凉拖  阅读(12)  评论(0编辑  收藏  举报