33、双向循环链表的实现

课程目标:使用linux内核链表实现双向循环链表

设计思路:数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点的定位。

实现思路:通过模板定义DualCircleList类,继承自DualLinkList类。

在DualCircleList内部使用Linux内核链表进行实现,使用struct list_head定义DualCircleList的头结点

特殊处理:循环遍历时忽略头结点。

实现要点:

通过list_head进行目标结点定位(position(i)),通过list_entry将list_head指针转换为目标结点指针。

通过list_for_each实现int find(const T& e)函数

遍历函数遍历函数中的next()和pre()需要考虑跳过头结点。

#ifndef DUALCIRCLELIST_H_
#define DUALCIRCLELIST_H_
#include "DualLinkList.h"
#include "LinuxList.h"
namespace WSlib
{
template <typename T>
class DualCircleList:public DualLinkList<T>
{
protected:
struct Node:public Wobject
{
list_head head;
T value;
};
list_head m_header;
list_head* m_current;
list_head* position(int i)const
{
list_head* ret=const_cast<list_head*>(&m_header);//const成员函数内部不允许直接取成员变量的地址
for(int p=0;p<i;p++)
{
ret=ret->next;
}
return ret;
}
int mod(int i) const
{
return (this->m_length==0)?0:(i % this->m_length);
}
public:
DualCircleList()
{
this->m_length=0;
this->m_step=1;
m_current=NULL;
INIT_LIST_HEAD(&m_header);
}
bool insert(const T& e)
{
return insert(this->m_length,e);
}
bool insert(int i,const T& e)
{
bool ret=true;
Node* node=new Node();
i=i%(this->m_length+1);
if(node!=NULL)
{
node->value=e;
list_add_tail(&node->head,position(i)->next);
this->m_length++;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"NO memory");
}
return ret;
}
bool remove(int i)
{
bool ret=true;
i=mod(i);
ret=((0<=i)&&(i<=this->m_length));
if(ret)
{
list_head* toDel=position(i)->next;
if(m_current==toDel)
{
m_current=toDel->next;
}
list_del(toDel);
this->m_length--;
delete list_entry(toDel,Node,head);
}
return ret;
}
bool set(int i,const T& e)  //o(n)
{
bool ret=true;
i=mod(i);
ret=((0<=i)&&(i<this->m_length));
if(ret)
{

/*Node* current=&m_header;
for(int p=0;p<i;p++)
{
current=current->next;
}*/
//Node* current=position(i);
//current->next->value=e;
list_entry(position(i)->next,Node,head)->value=e;
}
return ret;
}
virtual T get(int i) const  //o(n)
{
T ret;
if(get(i,ret))
{
return ret;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException,"Invalid parameter i to get element...");
}
return ret;
}
bool get(int i,T& e) const  //o(n)
{
bool ret=true;
i=mod(i);
ret=((0<=i)&&(i<m_length));
if(ret)
{
/*Node* current=&m_header;  //由于当前成员函数是const成员函数,不允许改变任何成员变量的值,&取地址符编译器认为是有可能修改成员变量的值,
for(int p=0;p<i;p++)      //解决:只需要在对应的成员变量前加mutable声明就可以了
{
current=current->next;
}*/
//Node* current=position(i);
//e=current->next->value;

e=list_entry(position(i)->next,Node,head)->value;  //因为是const成员函数,不可以调用position,所以position函数得定义为const成员函数
}
return ret;
}


int find(const T& e) const  //o(n)
{
int ret=-1;
int i=0;
list_head* slider=NULL;
list_for_each(slider,&m_header)
{
if(list_entry(slider,Node,head)->value==e)
{
ret=i;
break;
}
i++;
}
return ret;
}
int length() const
{
return this->m_length;
}
void clear()
{
while(this->m_length>0)
{
remove(0);
}
}
bool move(int i,int step=1)
{
bool ret=(step>0);
i=mod(i);
ret=ret&&((0<=i)&&(i<this->m_length));
if(ret)
{
m_current=position(i)->next;
this->m_step=step;
}
return ret;
}
bool end()
{
return (m_current==NULL||(m_length==0));


}

T current()
{
if(!end())
{
return list_entry(m_current,Node,head)->value;
}
else 
{
THROW_EXCEPTION(InvalidOperationException, "No value at current position ...");
}
}


bool next()
{
int i=0;
while((i<this->m_step)&& (!end()))   //循环m_step次,并且游标不为空
{
if(m_current!=&m_header)
{
m_current=m_current->next;
i++;
}
else
{
m_current=m_current->next;
}
}
if(m_current==&m_header)
{
m_current=m_current->next;
}
return(i==this->m_step); //表示移动成功
}
virtual bool pre()
{
int i=0;
while((i<this->m_step)&& (!end()))   //循环m_step次,并且游标不为空
{
if(m_current!=&m_header)
{
m_current=m_current->prev;
i++;
}
else
{
m_current=m_current->prev;
}
}
if(m_current==&m_header)
{
m_current=m_current->prev;
}
return(i==this->m_step); //表示移动成功
}
~DualCircleList()
{
clear();
}
};
}
#endif
/*************************************************************************************
#include <iostream>
#include "DualCircleList.h"
using namespace WSlib;
using namespace std;
int main()
{
DualCircleList<int> c1; //如果说int跟T不匹配,可能是拼写错误 大小写
for(int i=0;i<5;i++)
{
c1.insert(i);
c1.insert(5);
}
c1.move(c1.length()-1);
while(c1.find(5)!=-1)
{
if(c1.current()==5)
{
cout<<c1.current()<<endl;
c1.remove(c1.find(c1.current())); //测试remove 和find
}
else
{
c1.pre();
}
}
cout<<endl;
int i=0;
for(c1.move(c1.length()-1);(i<c1.length())&&!c1.end();c1.pre(),i++)
{
cout<<c1.current()<<endl;
}
return 0;
}
*********************************************************************************/

linux内核链表是带头结点的双向循环链表,DualCircleList使用Linux内核链表进行内部实现,DualCircleList在循环遍历时需要跳过头结点,将list_head指针转换为目标结点指针时,使用list_entry宏。

思考:下面代码中的pn1和pn2是否相等?

struct Node:public Wobject

{

list_head head;

T value;

};

Node node;

list_head* ld=&node.head;

Node* pn1=reinterpret_cast<Node*>(ld);

Node* pn2=list_entry(ld,Node,head);

不相等,虚函数表存在于对象实例中最前面的位置,虚函数表的存在使得地址不一样

问题:

1,struct Node{

struct list_head head;

int value;}

list_head* slider=NULL;

((struct Node*)slider)->value;

slider指向的head处于Node第一个位置,访问的value在下边,上边转换合法。

2,struct Node{

int value;

struct list_head head;};

struct Node l={0};

struct list_head* list=(struct list_head*)&l;-> 这里就不和法了,

应该改为:struct list_head* list=&l.head;

printf("%d\n",((struct Node*)slider)->value);->这里也不对,

应该改为:printf("%d\n",list_entry(slider,struct Node, head)->value);

slider指向Node中的head成员。


猜你喜欢

转载自blog.csdn.net/ws857707645/article/details/80452718