课程目标

  • 完成 StaticList 类的具体实现
  • 完成 DynamicList 类的具体实现

image.png

StaticList 设计要点

  • 类模板

    • 使用原生数组作为顺序存储空间
    • 使用模板参数决定数组大小
template <typename T, int N>
class StaticList : public SeqList<T>
{
public:
    StaticList();    // 指定父类成员的具体值
    int capacity() const;
protected:
    T m_space[N];    // 顺序存储空间,N为模板参数
};

编程实验:StaticList 的实现

文件:StaticList.h

#ifndef STATICLIST_H
#define STATICLIST_H

#include "SqlList.h"

namespace DTLib
{

template <typename T, int N>
class StaticList : public SqlList<T>
{
public:
    StaticList()
    {
        this->m_array = m_space;
        this->m_length = 0;
    }

    int capacity() const override
    {
        return N;
    }

protected:
    T m_space[N];
};

}

#endif // STATICLIST_H

文件:main.cpp

#include <iostream>
#include "StaticList.h"

using namespace std;
using namespace DTLib;

int main()
{
    cout << "main begin" << endl;

    StaticList<int, 5> sl;

    for (int i = 0; i < sl.capacity(); ++i)
    {
        sl.insert(0, i);
    }

    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "--------------" << endl;

    sl[0] *= sl[0];

    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "--------------" << endl;

    try
    {
       sl[5] = 5;
    }
    catch (const Exception &e)
    {
        cout << e.message() << endl;
        cout << e.location() << endl;
    }

    cout << "main end" << endl;

    return 0;
}

输出:

main begin
4
3
2
1
0
--------------
16
3
2
1
0
--------------
Parameter i is invalid ...
..\DTLib\SqlList.h:92
main end

DynamicList 设计要点

  • 类模板

    • 申请连续堆空间作为顺序存储空间
    • 动态设置顺寻存储空间的大小
    • 保证重置顺序存储空间时的异常安全性
  • 函数异常安全的概念

    • 不泄露任何资源
    • 不允许破坏数据
  • 函数异常安全的基本保证

    • 如果异常被抛出

      • 对象内的任何成员仍然能保持有效状态
      • 没有数据的破坏及资源泄漏
template <typename T>
class DynamicList : public SqlList<T>
{
public:
    DynamicList(int capacity)  // 申请空间
    int capacity() const;
    void resize(int capacity); // 重新设置存储空间的大小
    ~DynamicList();            // 归还空间

protected:
    int m_capacity;  // 顺序存储空间的大小
};

编程实验:DynamicList 的实现

文件:DynamicList.h

#ifndef DYNAMICLIST_H
#define DYNAMICLIST_H

#include "SqlList.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class DynamicList : public SqlList<T>
{
public:
    DynamicList(int capacity)
    {
        this->m_array    = new T[capacity];
        if (this->m_array != nullptr)
        {
            this->m_capacity = capacity;
            this->m_length   = 0;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
        }
    }

    int capacity() const override
    {
        return m_capacity;
    }

    void resize(int capacity)
    {
        if (capacity != m_capacity)
        {
            T *array = new T[capacity];             // 注意 1
            if (array != nullptr)
            {
               int length = (this->m_length < capacity) ? this->m_length : capacity;

                for (int i = 0; i< length; ++i)
                {
                    array[i] = this->m_array[i];    // 注意 2
                }

                T *temp = this->m_array;            // 注意 3

                this->m_array    = array;
                this->m_length   = length;
                this->m_capacity = capacity;

                delete [] temp;                     // 注意 3
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No meomry to resize DynamicList object ...");
            }
        }
    }

    ~DynamicList()
    {
        delete [] this->m_array;
    }

protected:
    int m_capacity = 0;
};

}

#endif // DYNAMICLIST_H

文件:main.cpp

#include <iostream>
#include "DynamicList.h"

using namespace std;
using namespace DTLib;

int main()
{
    cout << "main begin" << endl;

    DynamicList<int> sl(5);

    for (int i = 0; i < sl.capacity(); ++i)
    {
        sl.insert(0, i);
    }

    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "--------------" << endl;

    sl[0] *= sl[0];

    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "--------------" << endl;

    try
    {
       sl[5] = 5;
    }
    catch (const Exception &e)
    {
        cout << e.message() << endl;
        cout << e.location() << endl;

        sl.resize(10);
        sl.insert(5, 50);
    }

    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "--------------" << endl;

    sl.resize(3);
    for (int i = 0; i< sl.length(); ++i)
    {
        cout << sl[i] << endl;
    }

    cout << "main end" << endl;

    return 0;
}

输出:

main begin
4
3
2
1
0
--------------
16
3
2
1
0
--------------
Parameter i is invalid ...
..\DTLib\SqlList.h:92
16
3
2
1
0
50
--------------
16
3
2
main end
注意 1:T *array = new T[capacity];

申请新的内存空间,对原内存空间中的数据进行拷贝,以保留原始值。

注意 2:array[i] = this->m_array[i];

T 为泛指类型,因此拷贝赋值操作符可能被重载并抛出异常;
当异常发生时,函数执行停止并返回,此时当前类的数据成员(m_array,m_length,m_capacity)未被破坏,线性表仍然合法有效;
会造成 array 指向的内存空间泄漏,交由使用者负责。

注意 3:T *temp = this->m_array;

当不借助临时指针变量时:

delete this->m_array;  // 注意

this->m_array    = array;
this->m_length   = length;
this->m_capacity = capacity;

T 为泛指类型,因此析构函数中可能抛出异常;
当异常发生时,函数执行停止并返回,此时当前类的数据成员(m_array,m_length,m_capacity)未被修改,依然保持原始值,但此时m_array指向的内容可能已发生改变,线性表不再合法;
为解决以上问题,引入临时指针变量,当类中数据成员全部完成状态更新后,再 delete 临时变量指向的内存空间。


问题:是否可以将 DynamicList 作为 StaticList 的子类实现?

不可以,反之也不可以;
两个类对于对于顺序存储空间的指定没有任何关系,因此在类层次中位于同一层。

小结

  • StaticList 通过模板参数定义顺序存储空间
  • DynamicList 通过动态内存申请定义顺序存储空间
  • Dyanmic 支持动态重置顺序存储空间的大小
  • DynamicList 中的 resize() 函数实现需要保证异常安全

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
737 声望139 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧