c++类模拟实现单向链表
本次使用c++中的类的语法来模拟实现单向链表,单向链表结构较为简单,一个节点中只存储数据和下一个节点的指针,本次实现仍然带有哨兵位头节点
本次实现的接口有增删展示与销毁,查改读者可作为练习自行模拟实现
以下是本次使用的节点结构体与类和其中成员函数和成员变量
#include<iostream>
#include<cstdlib>
#include<cassert>
using namespace std;
typedef int LLtype;
struct List
{
LLtype x;
List* next;
};
class list
{
private:
List*head;
//创建一个新节点
List* Create_Node(LLtype x);
//销毁链表
void delt();
public:
//默认构造函数
list();
//有参构造函数
list(List*head);
//头插
void push_front(LLtype x);
//尾插
void push_back(LLtype x);
//展示链表
void Show();
//头删
void pop_front();
//尾删
void pop_back();
//析构函数
~list();
};
本次模拟实现会使用到assert进行报错判断,包含了cassert头文件
1.对于private成员的设置,目的是不在类外进行访问,尽量通过提供的public成员进行操作
2.默认构造函数
创建类之初会自动调用的默认构造函数,我们在这里对其进行的操作是将head指针赋值为NULL
list()
{
head=NULL;
}
3.有参构造函数
使用有参构造函数是为了对类进行初始化,给head赋值为我们在main函数中使用malloc开辟的一块空间
//有参构造函数
list(List*head)
{
this->head=head;
this->head->next=NULL;
}
4.创建一个节点
在增添元素时我们都会创建一个新节点,将其写为一个函数方便复用
//创建一个新节点
List* Create_Node(LLtype x)
{
List*newnode=(List*)malloc(sizeof(List));
newnode->x=x;
newnode->next=NULL;
return newnode;
}
返回结构体指针在函数内使用对应指针进行接受即可
5.头插
void push_front(LLtype x)
{
assert(this->head);
List*newnode=Create_Node(x);
List*next=head->next;
head->next=newnode;
newnode->next=next;
}
这里使用了assert对head进行断言,head作为哨兵位头节点是一定不能为空指针的
接下来改变对应链接关系即可,同样的有哨兵位头节点的存在,无需对新的实际头节点进行操作,关于改变对应连接关系可参考上一篇双链表的实现有详细说明
6.尾插
void push_back(LLtype x)
{
assert(this->head);
List*tail=head;
while(tail->next)
tail=tail->next;
List*newnode=Create_Node(x);
tail->next=newnode;
}
同样的,找到尾之后进行链接关系的改变即可
7.展示链表
//展示链表
void Show()
{
List*cur=head->next;
while(cur)
{
cout<<cur->x<<"->";
cur=cur->next;
}
cout<<"NULL"<<endl;
}
head指针作为哨兵位头节点不存储实际数据,从head->next开始进行遍历输出
8.头删
//头删
void pop_front()
{
assert(this->head);
assert(this->head->next);
List*next=head->next->next;
free(head->next);
head->next=next;
}
头删删的时哨兵位头节点的下一个,所以head不能为空,同样的链表内要有元素才能删除,所以head->next也不能为空,所以对二者都进行断言,注意存储下一个节点的指针,这样free掉原先实际头节点后仍有新的头节点的指针,接着改变链接关系即可
9.尾删
//尾删
void pop_back()
{
assert(this->head);
assert(this->head->next);
List*tail=head->next;
List*prev=NULL;
while(tail->next)
{
prev=tail;
tail=tail->next;
}
free(tail);
prev->next=NULL;
}
与头删类似,存储尾节点的前一个节点,新的尾节点要置空,同样的,哨兵位头节点不能为空,实际头节点也不能为空才有元素可以删除
10.销毁链表函数
注意这里是说他是销毁链表函数,是因为我们只写他,而不调用他,调用他的操作交给编译器完成(即析构函数)
void delt()
{
List*next=NULL;
List*cur=head->next;
while(cur)
{
next=cur->next;
free(cur);
cur=next;
}
free(head);
}
同样的,把他写在private作用域下是不希望在类外调用该函数
11.析构函数
~list()
{
delt();
}
在析构函数中我们直接调用刚刚完成的delt函数对链表进行销毁,这是因为编译器会在程序结束后调用析构函数,这样即释放了malloc出的空间,也在一个合适的时机对链表进行销毁而且是一定会销毁,避免了内存泄漏的问题
本次实现的接口就这么多,在类内实现便于我们对成员函数的调用,是一种非常便利的操作,读者也可尝试使用类进行双向循环链表的模拟,这也是c++STL中list容器中的链表的原理
最后感谢大家的阅读,欢迎各位读者批评指错
4278





