好久没来学堂了,主要是因为过去的一年里忙得多,学得少,如今我面对着巨大的压力,才深深地感受到学无止境的深切含义。闲话不多说了,回归正题:一个简单的模板链表类。
前段时间,回顾了下C++的模板类的知识,一时兴起,写了个简单的模板链表类,在vs2010下测试通过,深感类模板的强大,再此和大家分享下,一起交流交流。
首先,我们先定义一个节点类:class Node,它提供了一些方法:
void InsertAfter( Node <T>* p ); //将节点p插入到当前节点之后
Node<T>* DeleteAfter(void); //删除当前节点后的节点,并返回指向该节点的指针
void JoinNodes( Node <T>* p ); //将节点指针p指向的链加到当前节点之后
Node<T>* NextNode(void) const; //返回指向当前节点的next域
下面是这个类的定义和成员函数的实现:
#ifndef NODE_CLASS
#define NODE_CLASS
template <class T>
class Node {
private:
Node<T>* next;
public:
T data;
Node();
Node(const T& item,Node <T>* prtnext=NULL);
void InsertAfter( Node <T>* p );
void JoinNodes( Node <T>* p ) { next = p; }
Node<T>* DeleteAfter(void);
Node<T>* NextNode(void) const;
};
template <class T>
Node <T>::Node():next(NULL) {}
template <class T>
Node <T>::Node(const T& item,Node<T>* ptrnext) {
data = item;
next = ptrnext;
}
//将节点p插入到当前节点之后
template <class T>
void Node<T>::InsertAfter(Node<T>* p) {
p->next = next;
next = p;
}
//删除当前节点后的节点,并返回指向该节点的指针
template <class T>
Node<T>* Node<T>::DeleteAfter(void) {
Node<T>* p = next;
//若当前节点是最后一个节点,返回空
if( p == NULL ) return NULL;
next = p->next;
return p;
}
template <class T>
Node<T>* Node<T>::NextNode(void) const {
return next;
}
#endif
在这个节点类的基础上,我们可以进一步地定义一个链表类:class Linklist,它提供了链表的一些常用的方法:
int GetLength();
int InsBefNode(T item); //在当前游标之前插入新节点,更新游标前驱,不改变当前游标
int InsAftNode(T item); //在当前游标之后插入新节点,不更新游标
int InsFrmHead(T item); //从表头插入节点,游标指向第一个节点
void DelNode();
Linklist <T>& operator =( Linklist <T>& L ); //重载赋值运算符
inline void ResetCursor(); //重置链表游标,使其指向第一个节点
inline T GetCurData(); //返回当前游标所指向节点的内容
void MoveNext(); //移动当前游标
void Traverse();
int Union(Linklist <T>& L);
下面是链表类成员函数的定义和实现:
#ifndef LINKLIST_CLASS
#define LINKLIST_CLASS
#define OK 1
#define FAIL 0
#include "NodeClass.h"
enum ErrorType { allocmemoryfailed,DeleteNULLNode };
template <class T>
class Linklist
{
public:
Linklist();
Linklist( Linklist <T>& L);
~Linklist();
int GetLength() { return length; }
int InsBefNode(T item);
int InsAftNode(T item);
int InsFrmHead(T item);
void DelNode();
Linklist <T>& operator =( Linklist <T>& L );
inline void ResetCursor();
void MoveNext()
{
p_cur = p_cur->NextNode();
ppre_cur = ppre_cur->NextNode();
}
inline T GetCurData() { return p_cur->data; }
void Traverse();
int Union(Linklist <T>& L);
private:
void Error(ErrorType error) const;
Node <T>* head;
Node <T>* rear;
Node <T>* p_cur; //链表当前游标
Node <T>* ppre_cur; //指向链表当前游标的前驱
int length;
};
char * ErrMsg[] = {
"Memory Allocation Failed!",
"Delete NULL Error!"
};
template <class T>
void Linklist <T>::Error(ErrorType error) const
{
cout<<ErrMsg[error]<<endl;
}
template <class T>
inline void Linklist <T>::ResetCursor()
{
ppre_cur = head;
p_cur = head->NextNode();
}
template <class T>
Linklist <T>::Linklist():head(NULL),rear(NULL),p_cur(NULL),ppre_cur(NULL),length(0)
{
Node <T>* p = new Node <T>();
if( p == NULL ) Error(allocmemoryfailed);
head = rear = ppre_cur = p;
}
template <class T>
Linklist <T>::Linklist(Linklist <T>& L)
{
length = L.length;
Node <T>* p = new Node <T>();
if( p == NULL ) Error(allocmemoryfailed);
head = rear = ppre_cur = p;
//重设游标p_cur,ppre_cur
L.ResetCursor();
while( L.p_cur )
{
Node <T>* tmp = new Node <T>( L.GetCurData() );
if( tmp == NULL ) Error(allocmemoryfailed);
p->InsertAfter(tmp);
p = p->NextNode();
L.MoveNext();
}
rear = p;
p_cur = ppre_cur->NextNode();
}
template <class T>
Linklist <T>::~Linklist()
{
ResetCursor();
while(p_cur)
{
DelNode();
}
//此时只有头结点
delete head;
head = rear = p_cur = ppre_cur = NULL;
}
//从表头插入节点,游标指向第一个节点
template <class T>
int Linklist <T>::InsFrmHead(T item)
{
Node <T>* tmp = new Node <T>(item);
if( tmp == NULL ) {
Error(allocmemoryfailed);
return FAIL;
}
head->InsertAfter(tmp);
length++;
if(length == 1) rear = tmp; //调用此方法插入第一个节点,rear应当初始化
p_cur = tmp;
return OK;
}
//在当前游标之后插入新节点,不更新游标
template <class T>
int Linklist <T>::InsAftNode( T item )
{
Node <T>* tmp = new Node <T>(item);
if( tmp == NULL ) {
Error(allocmemoryfailed);
return FAIL;
}
if(!p_cur)
{
//刚初始化,p_cur未赋值 || 链表游标移动到最后了
ppre_cur->InsertAfter(tmp);
p_cur = rear = tmp;
}
else
{
p_cur->InsertAfter(tmp);
if(!(p_cur->NextNode()))
{
//若游标指向最后一个节点
rear = tmp;
}
}
length++;
return OK;
}
//在当前游标之前插入新节点,更新游标前驱,不改变当前游标
template <class T>
int Linklist <T>::InsBefNode( T item )
{
Node <T>* tmp = new Node <T>(item);
if( tmp == NULL ) {
Error(allocmemoryfailed);
return FAIL;
}
ppre_cur->InsertAfter(tmp);
length++;
ppre_cur = ppre_cur->NextNode();
if(!p_cur)
{
//刚初始化,p_cur未赋值 || 链表游标移动到最后了
rear = ppre_cur;
}
return OK;
}
//删除当前游标所指向的节点
template <class T>
void Linklist <T>::DelNode()
{
Node <T>* p = ppre_cur->DeleteAfter();
if(p)
{
length--;
p_cur = p_cur->NextNode();
if(!p_cur)
{
//游标指向最后一个节点
rear = ppre_cur;
}
delete p;
}
else
{
Error(DeleteNULLNode);
}
}
template <class T>
void Linklist <T>::Traverse()
{
cout<<"Traverse Linklist:"<<endl;
while(p_cur)
{
cout<<"["<<GetCurData()<<"]->";
MoveNext();
}
cout<<"NULL"<<endl;
ResetCursor();
}
template <class T>
Linklist <T>& Linklist <T>::operator =( Linklist <T>& L)
{
length = L.length;
L.ResetCursor();
ResetCursor();
while( L.p_cur && p_cur )
{
p_cur->data = L.GetCurData();
L.MoveNext();
MoveNext();
}
if(L.p_cur) //L比当前链表长
{
while(L.p_cur)
{
InsAftNode(L.GetCurData());
MoveNext();
L.MoveNext();
}
}
else if(p_cur) //L比当前链表短
{
while(p_cur)
{
DelNode();
}
}
else {}
ResetCursor();
return *this;
}
//合并链表,并且重设游标
template <class T>
int Linklist <T>::Union(Linklist <T>& L)
{
length += L.length;
//重设L的游标
L.ResetCursor();
rear->JoinNodes(L.p_cur);
rear = L.rear;
//将链表L的头指针next域赋空值
L.p_cur = L.rear = NULL;
(L.head)->JoinNodes(NULL);
ResetCursor();
return OK;
}
#endif
有了以上的定义我们就可以编写基本的应用了。而链表类模板的优势就在于我们可以用它来建立存放几乎任何类型数据的链表,而且不用对链表和节点的定义(也就是以上
的代码)进行任何改动,很吸引人吧!比如我们可以用以上的代码新建一个int型,float型,double型,或者char型的单链表(这里的int型……指的是数据域的类型,下同),甚至
可以新建string型,someclass型的链表,当然,自定义类型能用上述代码的前提是对<<,>>运算符做了重载。如果自定义类型开辟了动态内存,为了安全起见,还必须对=运算符做
重载,而且必须定义自己的拷贝构造函数。
下面我们就来演示下,利用上述代码建立一个student类型的链表:
首先,我们要定义一个student类:class student,对于他的方法和属性,不做详细介绍,下面是student类的定义和实现:
#ifndef STUDENT_CLASS
#define STUDENT_CLASS
#include <iostream>
using namespace std;
#define STU_NAME_MAX_LENGTH 50
class student
{
public:
student();
~student();
student(student&);
void SetName(const char*);
student& operator = (const student&);
friend ostream& operator << (ostream& os,const student& stu);
friend istream& operator >> (istream& is,student& stu);
private:
int stu_id;
char* stu_name;
};
student::student():stu_id(0),stu_name(NULL)
{
stu_name = new char[STU_NAME_MAX_LENGTH];
if(!stu_name)
{
throw("alloc memory error!\n");
}
stu_name[0] = '\0';
}
//这是必须的
student::student(student& stu)
{
stu_id = stu.stu_id;
stu_name = new char[STU_NAME_MAX_LENGTH];
if(!stu_name)
{
throw("alloc memory error!\n");
}
stu_name[0] = '\0';
strcpy(stu_name,stu.stu_name);
}
student::~student()
{
delete [] stu_name;
}
void student::SetName(const char* name)
{
if(strlen(name)>STU_NAME_MAX_LENGTH)
{
cout<<"Too Long Name."<<endl;
return ;
}
strcpy(stu_name,name);
}
student& student::operator =(const student& stu)
{
stu_id = stu.stu_id;
strcpy(stu_name,stu.stu_name);
return *this;
}
ostream& operator << (ostream& os,const student& stu)
{
os<<stu.stu_id<<","<<stu.stu_name;
return os;
}
istream& operator >> (istream& is,student& stu)
{
is>>stu.stu_id>>stu.stu_name;
if(!is)
{
stu = student();
}
return is;
}
#endif
到此为止,我们所有需要的类都已经定义完毕,让我们开始享受调用这些代码的乐趣吧!下面开始建立student型链表:
Linklist <student> L;
完毕!就这么简单,可是我们还没看到任何应用,难免有遗憾,下面我们就通过一个例子来体验下,并且顺便测试下应用。
//头文件部分省略……
int _tmain(int argc, _TCHAR* argv[])
{
try
{
Linklist <student> L;
cout<<"Please input the elements of L:"<<endl;
student data;
for(int i=0;i<3;i++)
{
cin>>data;
L.InsFrmHead(data);
}
L.Traverse();
Linklist <student> link(L);
cout<<"linklist link:"<<endl;
link.Traverse();
link.Union(L);
cout<<"link length:"<<link.GetLength()<<endl;
link.Traverse();
Linklist <student> somelink;
somelink = link;
cout<<"After somelink = link,somelink:"<<endl;
somelink.Traverse();
}
catch(string s)
{
cout<<s<<endl;
}
getchar();
getchar();
return 0;
}
到此为止,我们已经实现了建立一个student类型的链表,并对其进行插入元素,遍历,复制,合并等操作,感受到类模版的强大了吗?
以上就是链表类模板的定义和使用,希望对大家有所帮助。