本节我们要实现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. HttpClient发送get,post接口请求

    HttpClient发送get post接口请求/*  * post  * @param url POST地址 * @param data POST数据NameValuePair[] * @retur ...

  2. mac3.0环境搭建

    export ANDROID_SDK_ROOT=/Users/sjxxpc/Documents/ADT/sdk export ANDROID_NDK_ROOT=/Users/sjxxpc/Docume ...

  3. cocos代码研究(21)Widget子类TextField学习笔记

    基础理论 一个接受用户输入的widget. 输入文本的渲染基于TextFieldTTF. 如果你想用系统控制行为,请使用EditBox来替代.继承自 Widget. 代码实践 //与占位符有关void ...

  4. 妙用Excel数据透视表和透视图向导,将二维数据转换为一维数据

    项目中,每年都会有各种经销商的各种产品目标数据导入,经销商和产品过多,手工操作过于单调和复杂.那有没有一种方式可以将复杂的二维数据转换为一维数据呢? 有,强大的Excel就支持此功能. 常用Excel ...

  5. 手把手教你学node.js 之使用 eventproxy 控制并发

    使用 eventproxy 控制并发 目标 建立一个 lesson4 项目,在其中编写代码. 代码的入口是 app.js,当调用 node app.js 时,它会输出 CNode(https://cn ...

  6. onsubmit不起作用的原因

    使用form表单,添加onsubmit="return check()",来实现提交前进行相关验证功能时,有时会出现js函数不起作用的情况, 此时可以检查相应的js函数check( ...

  7. HDU - 2844 Coins(多重背包+完全背包)

    题意 给n个币的价值和其数量,问能组合成\(1-m\)中多少个不同的值. 分析 对\(c[i]*a[i]>=m\)的币,相当于完全背包:\(c[i]*a[i]<m\)的币则是多重背包,考虑 ...

  8. netty4.1.6源码2-------创建服务端的channel

    1. netty在哪里调用jdk底层的socket去创建netty服务端的socket. 2. 在哪里accept连接. 服务端的启动: 1. 调用jdk底层的api去创建jdk的服务端的channe ...

  9. 前端学习笔记之CSS介绍

    阅读目录 一 什么是CSS 二 为何要用CSS 三 如何使用CSS 一 什么是CSS CSS全称Cascading Style Sheet层叠样式表,是专用用来为HTML标签添加样式的. 样式指的是H ...

  10. Python3.x:定时获取页面数据存入数据库

    Python3.x:定时获取页面数据存入数据库 #间隔五分钟采集一次数据入库 import pymysql import urllib.request from bs4 import Beautifu ...