zoukankan      html  css  js  c++  java
  • 第十七课 StaticList和DynamicList实现

    本节我们要实现StaticList和DynamicList,如下:

    StaticList的设计要点:

    StaticList是一个类模板,使用原生数组作为顺序存储空间,使用模板参数决定数组大小

      在StaticList的类模板中我们定义了一个元素数组作为顺序存储空间。这就是static的含义。因此,需要在构造函数当中将这个顺序存储空间挂接到父类的m_array上。

    StaticList.h如下:

     1 #ifndef STATICLIST_H
     2 #define STATICLIST_H
     3 
     4 #include "SeqList.h"
     5 
     6 namespace DTLib
     7 {
     8 
     9 template < typename T, int N >
    10 class StaticList : public SeqList<T>
    11 {
    12 protected:
    13     T m_space[N];    //顺序存储空间,N为模板参数
    14 public:
    15     StaticList()    //指定父类成员的具体值
    16     {
    17         this->m_array = m_space;
    18         this->m_length = 0;
    19     }
    20 
    21     int capacity() const
    22     {
    23         return N;
    24     }
    25 };
    26 
    27 }
    28 
    29 #endif // STATICLIST_H

    main函数测试程序如下:

     1 #include <iostream>
     2 #include "List.h"
     3 #include "SeqList.h"
     4 #include "StaticList.h"
     5 
     6 using namespace std;
     7 using namespace DTLib;
     8 
     9 
    10 int main()
    11 {
    12     StaticList<int, 5> l;
    13 
    14     for(int i = 0; i < l.capacity(); i++)
    15     {
    16         l.insert(0, i);
    17     }
    18 
    19     for(int i = 0; i < l.capacity(); i++)
    20     {
    21         cout << l[i] << endl;
    22     }
    23 
    24     return 0;
    25 }

    执行结果如下:

    更改测试程序如下:

     1 #include <iostream>
     2 #include "List.h"
     3 #include "SeqList.h"
     4 #include "StaticList.h"
     5 #include "Exception.h"
     6 
     7 using namespace std;
     8 using namespace DTLib;
     9 
    10 
    11 int main()
    12 {
    13     StaticList<int, 5> l;
    14 
    15     for(int i = 0; i < l.capacity(); i++)
    16     {
    17         l.insert(0, i);
    18     }
    19 
    20     for(int i = 0; i < l.capacity(); i++)
    21     {
    22         cout << l[i] << endl;
    23     }
    24 
    25     try
    26     {
    27         l[5] = 5;
    28     }
    29     catch(IndexOutOfBoundsException& e)
    30     {
    31         cout << e.message() << endl;
    32         cout << e.location() << endl;
    33     }
    34 
    35     return 0;
    36 }

    第27行我们执行越界赋值操作,执行结果如下:

     DynamicList的设计:

    使用类模板

      申请连续堆空间作为顺序存储空间(StaticList使用的存储空间是“栈空间”(当我们定义的StaticList类对象也在栈上时))

      动态设置顺序存储空间的大小(StaticList中的存储空间大小是固定的,不能动态设置)

      保证重置顺序存储空间时的异常安全性

    DynamicList中的存储空间是动态申请的,而且可以动态设置大小,实现上会比StaticList复杂。

    DynamicList的设计要点:

    函数异常安全的概念

      不泄露任何资源

      不允许破坏数据

    函数异常安全的基本保证

      如果异常被抛出

        对象内的任何成员仍然能保持有效状态

        没有数据的破坏及资源泄漏

     

      DynamicList中就没有原生数组了,只有一个m_capacity代表存储空间的大小。这个大小就不是通过模板参数来指定了,而是通过构造函数的参数来指定。在构造函数中申请堆空间。此外,还是先了resize函数,用于设置顺序存储空间的大小。在析构函数中归还空间。

     DynamicList.h程序如下:

     1 #ifndef DYNAMICLIST_H
     2 #define DYNAMICLIST_H
     3 
     4 #include "SeqList.h"
     5 #include "Exception.h"
     6 
     7 namespace DTLib
     8 {
     9 
    10 template <typename T>
    11 class DynamicList : public SeqList<T>
    12 {
    13 protected:
    14     int m_capacity;    //顺序存储空间的大小
    15 public:
    16     DynamicList(int capacity)   //申请空间
    17     {
    18         this->m_array = new T(capacity);
    19 
    20         if( this->m_array != NULL)
    21         {
    22             this->m_length = 0;
    23             this->m_capacity = capacity;
    24         }
    25         else
    26         {
    27             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
    28         }
    29     }
    30 
    31     int capacity() const
    32     {
    33         return m_capacity;
    34     }
    35 
    36     //重新设置存储空间的大小
    37     void resize(int capacity)
    38     {
    39         if(capacity != m_capacity)
    40         {
    41             T* array = new T[capacity];
    42 
    43             if(array != NULL)
    44             {
    45                 int length = (this->m_length < capacity ? this->m_length : capacity);
    46 
    47                 for(int i = 0; i < length; i++)
    48                 {
    49                     array[i] = this->m_array[i];   //这里必须用this->m_array,不能直接用m_array,其他在父类中定义的成员同理
    50                 }
    51 
    52                 T* temp = this->m_array;
    53 
    54                 this->m_array = array;
    55                 this->m_length = length;
    56                 this->m_capacity = capacity;
    57 
    58                 delete[] temp;
    59             }
    60             else
    61             {
    62                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
    63             }
    64         }
    65     }
    66 
    67     ~DynamicList()
    68     {
    69         delete[] this->m_array;
    70     }
    71 };
    72 
    73 }
    74 
    75 #endif // DYNAMICLIST_H

      第41行中我们没有直接操作this->m_array,因为重置空间后我们原来的数据元素还需要保留,因此,我们在47-50行中将原来的数据元素拷贝到新空间中。所以,我们不能让m_array指向新申请的空间,必须让它指向原来的存储空间。

      52行我们定义了一个临时指针指向原来的存储空间,到了58行才delete原来的空间。为什么要这样做呢?因为delete有可能触发数据元素的析构函数,而在这个析构函数中有可能抛出异常。如果我们在52行直接delete this->m_array,就有可能触发析构函数,并在析构对象是引发异常(数据元素对象是用户定义的),函数就在52行异常返回了,就意味着下面的赋值操作全都不能执行到,那样的话当前的线性表就不是合法可用的了。也就是这个resize函数就不是异常安全的了。而用这种临时指针的方式,可以保证54-57被执行到,而且这三行不会发生异常。执行完这些赋值,再执行58行,即使这时候发生异常,那m_array,m_length,m_capacity也已经是合法的了,也可以保证我们的线性表对象也是合法可用的,这就做到了异常安全。

      第49行的赋值操作也有可能发生异常,在这里发生异常,m_array,m_length,m_capacity这几个成员变量的值没有发生任何改变,所以当前这个线性表对象依然是合法可用的。只是array指向的堆空间会被泄漏。这一点对于resize函数来说就无法顾全了,因为数据类型T是用户指定的,赋值操作符也可能被重载,并且重载的函数中可能发生异常,这些我们都无法顾全。这是第三方工程师代码的问题。这样的问题只能交给第三方工程师自己来考虑。

     main函数测试程序如下:

     1 #include <iostream>
     2 #include "List.h"
     3 #include "SeqList.h"
     4 #include "StaticList.h"
     5 #include "Exception.h"
     6 #include "DynamicList.h"
     7 
     8 using namespace std;
     9 using namespace DTLib;
    10 
    11 
    12 int main()
    13 {
    14 
    15     DynamicList<int> l(5);
    16 
    17     for(int i = 0; i < l.capacity(); i++)
    18     {
    19         l.insert(0, i);
    20     }
    21 
    22     for(int i = 0; i < l.length(); i++)
    23     {
    24         cout << l[i] << endl;
    25     }
    26 
    27     try
    28     {
    29         l[5] = 5;
    30     }
    31     catch(const Exception& e)
    32     {
    33         cout << e.message() << endl;
    34         cout << e.location() << endl;
    35 
    36         l.resize(10);
    37         l.insert(5, 50);
    38     }
    39 
    40     l[5] = 5;
    41 
    42     for(int i = 0; i < l.length(); i++)
    43     {
    44         cout << l[i] << endl;
    45     }
    46 
    47     return 0;
    48 }

    执行结果如下:

    DynamicList不能作为StaticList的子类实现,反之也是不可以的,因为这两个类对于顺序存储空间的指定是没有任何关系的,截然相反的。

    小结:

      StaticList通过模板参数定义顺序存储空间,并且将原生的数组作为顺序存储空间使用

      DynamicList通过动态内存申请定义顺序存储空间

      DynamicList支持动态重置顺序存储空间的大小

      DynamicList中的resize函数实现需要保证异常安全

  • 相关阅读:
    Sublime text 2 编辑器
    SWF文件版本支持映射表
    sql SET DATEFORMAT对日期处理的影响
    关于 getQualifiedDefinitionNames() 的使用场景
    prototypephotoarchive.cs
    jQuery easyui layout布局自适应浏览器大小
    iteratorfamilytree.cs
    内存利用率
    SET LANGUAGE对日期处理的影响示例
    mbox:Unix 的入门级邮箱
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9501825.html
Copyright © 2011-2022 走看看