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

StaticList的设计要点:

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

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

StaticList.h如下:

 #ifndef STATICLIST_H
#define STATICLIST_H #include "SeqList.h" namespace DTLib
{ template < typename T, int N >
class StaticList : public SeqList<T>
{
protected:
T m_space[N]; //顺序存储空间,N为模板参数
public:
StaticList() //指定父类成员的具体值
{
this->m_array = m_space;
this->m_length = ;
} int capacity() const
{
return N;
}
}; } #endif // STATICLIST_H

main函数测试程序如下:

 #include <iostream>
#include "List.h"
#include "SeqList.h"
#include "StaticList.h" using namespace std;
using namespace DTLib; int main()
{
StaticList<int, > l; for(int i = ; i < l.capacity(); i++)
{
l.insert(, i);
} for(int i = ; i < l.capacity(); i++)
{
cout << l[i] << endl;
} return ;
}

执行结果如下:

更改测试程序如下:

 #include <iostream>
#include "List.h"
#include "SeqList.h"
#include "StaticList.h"
#include "Exception.h" using namespace std;
using namespace DTLib; int main()
{
StaticList<int, > l; for(int i = ; i < l.capacity(); i++)
{
l.insert(, i);
} for(int i = ; i < l.capacity(); i++)
{
cout << l[i] << endl;
} try
{
l[] = ;
}
catch(IndexOutOfBoundsException& e)
{
cout << e.message() << endl;
cout << e.location() << endl;
} return ;
}

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

DynamicList的设计:

使用类模板

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

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

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

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

DynamicList的设计要点:

函数异常安全的概念

  不泄露任何资源

  不允许破坏数据

函数异常安全的基本保证

  如果异常被抛出

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

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

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

DynamicList.h程序如下:

 #ifndef DYNAMICLIST_H
#define DYNAMICLIST_H #include "SeqList.h"
#include "Exception.h" namespace DTLib
{ template <typename T>
class DynamicList : public SeqList<T>
{
protected:
int m_capacity; //顺序存储空间的大小
public:
DynamicList(int capacity) //申请空间
{
this->m_array = new T(capacity); if( this->m_array != NULL)
{
this->m_length = ;
this->m_capacity = capacity;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
}
} int capacity() const
{
return m_capacity;
} //重新设置存储空间的大小
void resize(int capacity)
{
if(capacity != m_capacity)
{
T* array = new T[capacity]; if(array != NULL)
{
int length = (this->m_length < capacity ? this->m_length : capacity); for(int i = ; i < length; i++)
{
array[i] = this->m_array[i]; //这里必须用this->m_array,不能直接用m_array,其他在父类中定义的成员同理
} T* temp = this->m_array; this->m_array = array;
this->m_length = length;
this->m_capacity = capacity; delete[] temp;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
}
}
} ~DynamicList()
{
delete[] this->m_array;
}
}; } #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函数测试程序如下:

 #include <iostream>
#include "List.h"
#include "SeqList.h"
#include "StaticList.h"
#include "Exception.h"
#include "DynamicList.h" using namespace std;
using namespace DTLib; int main()
{ DynamicList<int> l(); for(int i = ; i < l.capacity(); i++)
{
l.insert(, i);
} for(int i = ; i < l.length(); i++)
{
cout << l[i] << endl;
} try
{
l[] = ;
}
catch(const Exception& e)
{
cout << e.message() << endl;
cout << e.location() << endl; l.resize();
l.insert(, );
} l[] = ; for(int i = ; i < l.length(); i++)
{
cout << l[i] << endl;
} return ;
}

执行结果如下:

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

小结:

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

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

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

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

第十七课 StaticList和DynamicList实现的更多相关文章

  1. NeHe OpenGL教程 第四十七课:CG顶点脚本

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第三十七课:卡通映射

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第二十七课:影子

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第十七课:2D图像文字

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. python六十七课——网络编程(基础知识了解)

    网络编程: 什么是网络编程? 网络:它是一种隐形的媒介:可以将多台计算机使用(将它们连接到一起) 网络编程:将多台计算机之间可以相互通信了(做数据交互) 一旦涉及到网络编程,划分为两个方向存在,一方我 ...

  6. 四、StaticList 和 DynamicList

    1.StaticList类模板 StaticList的设计要点:类模板 使用原生数组作为顺序存储空间 使用模板参数决定数组大小 template <typename T, int N> c ...

  7. Python学习第十七课——组合

    组合1 #组合 1 class Hand: pass class Foot: pass class Trunk: pass class Head: pass class Person: def __i ...

  8. 如何使用webgl(three.js)实现3D储能,3D储能站,3D智慧储能、储能柜的三维可视化解决方案——第十七课

    前言 上节课我们讲了<3D光伏发电>,与之配套的就是能量存储 这节课我们主要讲讲储能,储能站,在分布式能源系统中起到调节用对电的尖峰平谷进行削峰填谷的作用.特别是小型储能站,更加灵活,因地 ...

  9. 斯坦福第十七课:大规模机器学习(Large Scale Machine Learning)

    17.1  大型数据集的学习 17.2  随机梯度下降法 17.3  微型批量梯度下降 17.4  随机梯度下降收敛 17.5  在线学习 17.6  映射化简和数据并行 17.1  大型数据集的学习

随机推荐

  1. R语言apply()函数用法

    在R语言的帮助文档里,apply函数的功能是: Retruns a vector or array or list of values obtained by applying a function ...

  2. router基本使用

    摘自:https://blog.csdn.net/qq_39894133/article/details/78992923 1.vue2 路由的使用流程: 1.vue2中的路由定义层:<rout ...

  3. javascript数组总结

    数组是一个有序的集合,javascript数组中的元素的类型可以是任意的,同一个数组不同元素之间的类型也是可以不同的.数组也是对象,有个length属性,记录数组的长度. 创建数组有两种方法: 数组直 ...

  4. NET Framework 4.0无法安装!

    win7旗舰版无法安装CAD2012,安装NET Framework 4.0的时候就出现错误,安装NET Framework 4.0单独版也无法安装出现错误. 解决方法: 1.点击电脑桌面右下角的“开 ...

  5. 【转载】Java动态代理之JDK实现和CGlib实现(简单易懂)

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6542259.html 一:代理模式(静态代理) 代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是 ...

  6. duilib : 滑动显示的窗口实现以及 悬浮窗 (转载)

    1. vc 判断窗口是否显示  BOOL IsWindowVisible(HWND hWnd); 2.悬浮窗 http://blog.csdn.net/lincyang/article/details ...

  7. git commit的规范

    https://www.yuque.com/fe9/basic/nruxq8#6c228def 制定一个 git commit 信息的提交规范是开发团队工作流必不可少的环节.试想一下,如果查看主分支上 ...

  8. sonar runner的配置

    #Configure here general information about the environment, such as SonarQube DB details for example ...

  9. 【Nature 子刊】I型HLA基因中和癌症相关的体细胞突变--转载

    肿瘤的发生与免疫系统的功能密切相关.在免疫系统中,MHC(主要组织相容性复体,majorhistocompatibilitycomplex)是所有生物相容复合体抗原的一种统称.HLA(humanleu ...

  10. Java 集合-Collections工具类

    2017-11-05 23:41:53 Collections类 Collections类:Collections类是针对集合进行操作的工具类,都是静态方法. 常用方法: public static ...