DynamicList类的设计要点:
同样使用类模板
继承自SeqList
申请连续的堆空间作为顺序存储空间
动态设置顺序存储空间的大小
保证重置顺序存储空间时的异常安全性。
什么叫做异常安全:不泄露任何资源和不允许破坏需要的数据。
函数异常安全的保障:如果有异常抛出时,对象内的任何成员仍然能保持有效状态,没有数据的破坏及资源泄漏。
先看总体设计:
template <typename T>
class DynamicList : public SeqList<T>
{
protected:
int m_capacity;//顺序存储的空间大小
public:
DynamicList(int capacity);//动态申请空间
int capacity() const;
void resize(int capacity);
~DynamicList();//归还空间
};
在构造函数中指定存储空间的大小,特别加了个重置空间大小的函数,实现中需要保证异常安全以保证原先的数据不会丢失
DynamicList(int capacity)//动态申请空间
{
this->m_array = new T[capacity];
if(this->m_array != NULL)
{
this->m_length = 0;
this->m_capacity = capacity;
}
else
{
//申请失败则抛出异常
}
}
实现就是在堆空间中申请capacity大小的T类型的数组,并将m_array指向它。
二、获取最大容量
int capacity() const
{
return m_capacity;
}
直接返回代表空间最大容量的m_capacity成员变量。
三、重置空间大小
//重新设置顺序存储空间的大小,保证异常安全(异常发生后数据不会丢失)
void resize(int capacity)
{
if(capacity != m_capacity )
{
T* array = new T[capacity]; //不直接操作this->m_array的目的就是保证原先的数据不会丢失
if(array != NULL)
{
int length = (this->m_length < capacity ? this->m_length : capacity);//操作长度为少的那个,不会出现越界的情况
for(int i = 0; i < length; i++)
{
//array多余的空间先空着等待数据插入
array[i] = this->m_array[i];//可能发生异常:由于T是泛指类型,当T是类类型时可能赋值操作符会被重载,
// 而重载函数中设有异常抛出时就可能发送异常,但是没办法,要是出现了异常就不是我们的事了。
}
T* temp = this->m_array;//使用temp的原因:如果直接在此处使用delete[]this->m_array可能
// 触发析构函数调用(T为类类型时)而抛出一些异常,程序也会从当前语句返回,那后面的语句就不会执行了
//如何会抛出异常?当析构函数中设置了异常抛出时,就可能抛出异常
this->m_array = array;
this->m_length = length;
this->m_capacity = capacity;
delete []temp;//此处delete保证了线性表的指向空间、当前表长、最大容量得到保存,所以数据不会丢失,保证了异常安全
}
else
{
//申请失败抛出异常
}
}
}
具体代码功能已经在程序所在位置明确注释,更方便理解。
四、释放空间
~DynamicList()//归还空间
{
delete [] this->m_array;
}
在析构函数中delete申请的空间即可。
测试代码和StaticList类几乎一样,只是他们的存储位置不同而已,其他操作仍然相同,详见上一篇测试代码。