1. 单链表的一个缺陷
触发条件:长时间使用单链表对象频繁增加和删除数据元素
可能的结果:堆空间产生大量的内存碎片,导致系统运行缓慢
2. 新的线性表
2.1 设计思路
在“单链表”的内部增加一片预留的空间,所有的Node对象都在这篇空间中动态创建和动态销毁。
2.2实现思路
- 通过模板定义静态单链表类(StaticLinkList)
- 在类中定义固定大小的空间(unsigned char [ ])
- 重写create 和 destroy函数,改变内存的分配和归还方式
- 在Node类中重载operator new ,用于在指定内存上创建对象
2.3代码
#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H
#include "linklist.h"
namespace DragonLib
{
template<typename T,int N>
class StaticLinkList : public LinkList<T>
{
protected:
typedef typename LinkList<T>::Node Node;
struct SNode : public Node
{
void* operator new(unsigned int size,void* loc)
{
(void)size;
return loc;
}
};
unsigned char m_space[sizeof(SNode) * N];
int m_used[N];
Node* create()
{
SNode* ret = NULL;
for(int i=0;i<N;i++)
{
if(!m_used[i])
{
ret = reinterpret_cast<SNode*>(m_space) + i;
ret = new(ret)SNode();
m_used[i] = 1;
break;
}
}
return ret;
}
void destroy(Node* pn)
{
SNode* space = reinterpret_cast<SNode*>(m_space);
SNode* psn = dynamic_cast<SNode*>(pn);
for(int i=0;i<N;i++)
{
if(pn == (space+i))
{
m_used[i] = 0;
psn->~SNode();
}
}
}
public:
StaticLinkList()
{
for(int i=0;i<N;i++)
{
m_used[i] = 0;
}
}
int capacity()
{
return N;
}
};
}
#endif // STATICLINKLIST_H
//main.cpp
#include <iostream>
#include "StaticLinkList.h"
using namespace std;
using namespace DragonLib;
int main()
{
StaticLinkList<int,5> list;
for(int i=0;i<5;i++)
{
list.insert(0,i);
}
try
{
list.insert(6);
}
catch(const Exception& e)
{
cout<< e.message()<<endl;
}
for(list.move(0);!list.end();list.next())
{
cout << list.current() << endl;
}
return 0;
}
插入五个数据元素之后,再次插入会抛异常:
我们来看看insert()函数:
bool insert(int i,const T& e)
{
bool ret = ((0<=i) && (i<=m_length));
if(ret)
{
Node* node = create();
if(node != NULL)
{
Node* current = position(i);//O(n)
node->value = e;
node->next = current->next;
current->next = node;
m_length++;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to insert new element!");
}
}
return ret;
}
我们可以发现里面有个create()函数,我们再来深入看看:
//LinkList.h
virtual Node* create()
{
return new Node();
}
我们发现它是一个虚函数,我们在LinkList的子类中对他进行实现,这是面向对象里多态的典型应用!
Node* create()
{
SNode* ret = NULL;
for(int i=0;i<N;i++)
{
if(!m_used[i])
{
ret = reinterpret_cast<SNode*>(m_space) + i;
ret = new(ret)SNode();
m_used[i] = 1;
break;
}
}
return ret;
}
此时N是5,我们要在第六个位置插数据,当然不行!所以抛异常
3. Q&A
LinkList中封装create和destroy函数的意义?
为静态链表StaticLinkList的实现做准备。StaticLinkList与LinkList的不同仅在于链表结点内存分配上的不同;因此,将仅有的不同封装于父类和子类的虚函数中。
4. 小结
- 顺序表与单链表相结合后衍生出静态单链表
- 静态单链表是LinkList的子类,拥有单链表的所有操作
- 静态单链表在预留的空间中创建结点对象
- 静态单链表适合于频繁增删数据元素的场合(最大元素个数固定)