课程目标
完成链式存储结构线性表的实现
LinkList 设计要点
- 类模板,通过头节点访问后继节点
- 定义内部节点类型 Node,用于描述数据域和指针域
- 实现线性表的关键操作(增,删,查,等)
LinkList 的定义
template
class LinkList : public List
{
public:
LinkList();
// ...
protected:
struct Node : public Object
{
T value;
Node *next;
};
Node m_header;
int m_length;
};
编程实验:链表的实现
文件:LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include "List.h"
#include "Exception.h"
namespace DTLib
{
template
class LinkList : public List
{
public:
LinkList()
{
m_header.next = nullptr;
m_length = 0;
}
bool insert(const T &e) override
{
return insert(m_length, e);
}
bool insert(int i, const T &e) override
{
bool ret = ((0 <= i) && (i <= m_length));
if (ret)
{
Node *node = new Node();
if (node != nullptr)
{
Node *current = &m_header;
for (int p=0; pnext;
}
node->value = e;
node->next = current->next;
current->next = node;
++m_length;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
}
}
return ret;
}
bool remove(int i) override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = &m_header;
for (int p=0; pnext;
}
Node *toDel = current->next;
current->next = toDel->next;
delete toDel;
--m_length;
}
return ret;
}
bool set(int i, const T &e) override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = &m_header;
for (int p=0; pnext;
}
current->next->value = e;
}
return ret;
}
T get(int i) const
{
T ret;
if (!get(i, ret))
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
}
return ret;
}
bool get(int i, T &e) const override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = &m_header;
for (int p=0; pnext;
}
e = current->next->value;
}
return ret;
}
int length() const
{
return m_length;
}
void clear()
{
while (m_header.next)
{
Node *toDel = m_header.next;
m_header.next = toDel->next;
delete toDel;
--m_length;
}
}
~LinkList()
{
clear();
}
protected:
struct Node : public Object
{
T value;
Node *next;
};
mutable Node m_header;
int m_length;
};
}
#endif // LINKLIST_H
文件:main.cpp
#include
#include "LinkList.h"
using namespace std;
using namespace DTLib;
int main()
{
cout << "main begin" << endl;
LinkList list;
for (int i=0; i<5; ++i)
{
list.insert(0, i);
list.set(0, i*i);
}
for (int i=0; i
输出:
main begin
16
9
4
1
0
------------
16
9
1
0
------------
main end
问题
头节点是否存在隐患?
实现代码是否需要优化?
代码优化
insert, remove, get, set 等操作都设计元素定位
Node *current = &m_header;
for (int p=0; pnext;
}
==>
Node *position(int i);
头节点的隐患
struct Node : public Object
{
T value;
Node *next;
};
==>
class Test
{
public:
Test()
{
throw 0;
}
};
会发生什么呢?
int main()
{
cout << "main begin" << endl;
LinkList list;
cout << "main end" << endl;
}
输出:
main begin
terminate called after throwing an instance of 'int'
使用者的疑问:
尽管 Test 类对象会抛出异常,可是我没有定义 Test 对象,只是定义了 LinkList
代码分析:
=> LinkList
触发成员对象的构造函数调用;
=> Node m_header;
触发成员对象的构造函数被调用;
=> Test
(泛指类型) 构造函数被调用,异常抛出。
解决方案:
避免在构造头节点 m_header 时对泛型对象 T 的构造,而又保证内存布局与Node类对象相同。
==》
头节点类型重定义。
struct Node : public Object
{
T value;
Node *next;
};
mutable struct : public Object
{
char reserved[sizeof (T)]; // 仅占位用
Node *next;
}m_header;
编程实验:链表的优化
文件:LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include "List.h"
#include "Exception.h"
namespace DTLib
{
template
class LinkList : public List
{
public:
LinkList()
{
m_header.next = nullptr;
m_length = 0;
}
bool insert(const T &e) override
{
return insert(m_length, e);
}
bool insert(int i, const T &e) override
{
bool ret = ((0 <= i) && (i <= m_length));
if (ret)
{
Node *node = new Node();
if (node != nullptr)
{
Node *current = position(i);
node->value = e;
node->next = current->next;
current->next = node;
++m_length;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
}
}
return ret;
}
bool remove(int i) override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = position(i);
Node *toDel = current->next;
current->next = toDel->next;
delete toDel;
--m_length;
}
return ret;
}
bool set(int i, const T &e) override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
position(i)->next->value = e;
}
return ret;
}
T get(int i) const
{
T ret;
if (!get(i, ret))
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
}
return ret;
}
bool get(int i, T &e) const override
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
e = position(i)->next->value;
}
return ret;
}
int length() const
{
return m_length;
}
void clear()
{
while (m_header.next)
{
Node *toDel = m_header.next;
m_header.next = toDel->next;
delete toDel;
--m_length;
}
}
~LinkList()
{
clear();
}
protected:
struct Node : public Object
{
T value;
Node *next;
};
mutable struct : public Object
{
char reserved[sizeof (T)];
Node *next;
}m_header;
int m_length;
Node *position(int i) const
{
Node *ret = reinterpret_cast(&m_header);
for (int p=0; pnext;
}
return ret;
}
};
}
#endif // LINKLIST_H
文件:main.cpp
#include
#include "LinkList.h"
using namespace std;
using namespace DTLib;
class Test
{
public:
Test()
{
throw 0;
}
};
int main()
{
cout << "main begin" << endl;
LinkList list;
cout << "main end" << endl;
return 0;
}
输出:
main begin
main end
小结
- 通过类模板实现链表,包含头节点成员和长度成员
- 定义节点类型,并通过堆中的节点对象构造链式存储
- 为了避免构造错误的隐患,头节点类型需要重定义
- 代码优化是编码完成后必不可少的环节
提示:只要代码发生改动,就需要重新测试
以上内容整理于狄泰软件学院系列课程,请大家保护原创!