第十七课 StaticList和DynamicList实现
本节我们要实现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实现的更多相关文章
- NeHe OpenGL教程 第四十七课:CG顶点脚本
		
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
 - NeHe OpenGL教程 第三十七课:卡通映射
		
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
 - NeHe OpenGL教程 第二十七课:影子
		
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
 - NeHe OpenGL教程 第十七课:2D图像文字
		
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
 - python六十七课——网络编程(基础知识了解)
		
网络编程: 什么是网络编程? 网络:它是一种隐形的媒介:可以将多台计算机使用(将它们连接到一起) 网络编程:将多台计算机之间可以相互通信了(做数据交互) 一旦涉及到网络编程,划分为两个方向存在,一方我 ...
 - 四、StaticList 和 DynamicList
		
1.StaticList类模板 StaticList的设计要点:类模板 使用原生数组作为顺序存储空间 使用模板参数决定数组大小 template <typename T, int N> c ...
 - Python学习第十七课——组合
		
组合1 #组合 1 class Hand: pass class Foot: pass class Trunk: pass class Head: pass class Person: def __i ...
 - 如何使用webgl(three.js)实现3D储能,3D储能站,3D智慧储能、储能柜的三维可视化解决方案——第十七课
		
前言 上节课我们讲了<3D光伏发电>,与之配套的就是能量存储 这节课我们主要讲讲储能,储能站,在分布式能源系统中起到调节用对电的尖峰平谷进行削峰填谷的作用.特别是小型储能站,更加灵活,因地 ...
 - 斯坦福第十七课:大规模机器学习(Large Scale Machine Learning)
		
17.1 大型数据集的学习 17.2 随机梯度下降法 17.3 微型批量梯度下降 17.4 随机梯度下降收敛 17.5 在线学习 17.6 映射化简和数据并行 17.1 大型数据集的学习
 
随机推荐
- Mybatis入门和简单Demo
			
一.Mybatis的诞生 回顾下传统的数据库开发,JDBC和Hibernate是使用最普遍的技术,但这两种ORM框架都存在一定的局限性: JDBC:最原生的技术,简单易学,执行速度快,效率高,适合大数 ...
 - 在jQuery中解决事件冒泡问题
			
<pre name="code" class="html">事件会按照DOM层次结构像水泡一样不断向上直至顶端 解决方法:在事件处理函数中返回fal ...
 - Swagger生成的接口需要权限验证的处理方法
			
通常开发API的时候需要对接口进行权限验证,而我们在使用Swagger生成接口文档界面的时候,直接调用需要权限验证的接口会提示"当前用户没有登陆" 为了解决此问题,我们需要更改一下 ...
 - ruby中的win32ole使用
			
ruby中的win32ole是一个标准库,使用的时候只要添加require 'win32ole'就行. 下面是一段模拟一个登陆的代码 require 'win32ole' ie = WIN32OLE. ...
 - 77. Combinations(回溯)
			
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. Example: I ...
 - Mysql优化原则_小表驱动大表IN和EXISTS的合理利用
			
//假设一个for循环 ; $i < ; $i++) { ; $i < ; $j++) { } } ; $i < ; $i++) { ; $i < ; $j++) { } } ...
 - bzoj1297 / P4159 [SCOI2009]迷路
			
P4159 [SCOI2009]迷路 如果边权只有 0/1 那么不就是一个灰常简单的矩阵快速幂吗! 然鹅边权 $<=9$ 所以我们把每个点拆成9个点! 解决~ #include<iostr ...
 - JAVA面试题整理(5)-数据库
			
数据库 1.Oracle/mysql分页有什么优化 2.悲观锁.乐观锁 悲观锁(Pessimistic Concurrency Control,PCC):假定会发生并发冲突,屏蔽一切可能违反数据完整性 ...
 - 2018-2019-1 20189215《Linux内核原理与分析》第二周作业
			
本周学习了<庖丁解牛>第1章,以及<Linux内核设计与实现>第1.2.18章.通过视频和实验,学会了反汇编一个简单的C程序,也学习了Linux内核调试的一些小技巧和print ...
 - 20145311王亦徐 实验三 "敏捷开发与XP实践"
			
20145311王亦徐 实验三 "敏捷开发与XP实践"程序设计过程 实验内容 使用 git 上传代码 使用 git 相互更改代码 实现代码的重载 git 上传代码 查看代码是否有修 ...