zoukankan      html  css  js  c++  java
  • 第二十二课 单链表的具体实现

    本节目标:

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

    结果如下:

    问题已然解决了。

    小结:

  • 相关阅读:
    安装xshell6
    eclipse的安装和汉化
    collectd+infludb+grafana实现tomcat JVM监控
    百度网站统计和CNZZ网站统计对比
    shell,计算指定行的和,计算指定列的和
    我为什么要写博客
    kafka监控之topic的lag情况监控
    用rundeck启动tomcat报错
    xwiki升级8.8.4
    矩阵掩膜操作
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9650109.html
Copyright © 2011-2022 走看看