C++ list 模拟实现

list的底层是一个带有哨兵位的头结点,可双向循环的链表。

list的优点在于:任意位置插入和删除效率高,不需要移动元素。list的主要缺点:它不支持随机访问某个元素

目录

1、结点的成员变量

2、迭代器

1>迭代器的三个模板参数

2、operator*   和   operator->

3、前置++,后置++,前置--,后置--

"=="和"!="的重载

3、list的普通迭代器和const迭代器

4.list的insert&erase

5.list的push_back, pop_back   push_front  pop_front  clear

6.list的构造函数,析构函数,拷贝和赋值


1、结点的成员变量

因为这个链表是带头结点可双向循环的链表,而链表的成员变量就是链表的节点,节点内含有指向上一个节点的指针和指向下一个结点的指针以及该结点的值。

template<class T>
struct  __list_node
{
  __list_node<T>* _next; //上一个结点的指针
  __list_node<T>* _prev; //下一个结点的指针
  T _data; //该节点内存储的数据

  __list_node(const T& x=T()) //构造函数
  :data(x);
  ,_next(nullptr)
  ,_prev(nullptr)
  {}

};

2、迭代器

list的迭代器并不是一个原生指针,因为list的底层是一个带有头结点且双向循环的链表,并非是一段连续的空间,当我们使用<<迭代器和++迭代器,迭代器并非不能够完成我们想要的结果。

所以我们要将迭代器封装成一个类,将++,*等其操作运算符进行重载

 

1>迭代器的三个模板参数

tepmplate<class T,class Ret,class Ptr> // 三个模板参数

struct __list_iterator
{
  typedef __list_node<T> Node;
  typedef ____list_iterator<T,Ret,Ptr> Self;
 
  Node* _node ;//成员变量

  Ref operator*()
  {
    return _node->_data;
  }
  
}

我们这里使用三个模板参数,我们先简单看一下const 迭代器和普通迭代器的模板使用

当我们的传的是普通迭代器时T 和 Ref 和 Ptr 分别代表 (T)、 (T& )、(T*)

当我们的传的是const迭代器·时T 和 Ref 和 Ptr 分别代表 (T)、 (const T& )、(const T*)

迭代器的成员变量就是一个节点的指针

迭代器的构造函数就是传过来一个节点的指针,将迭代器指向节点就可以

2、operator*   和   operator->

tepmplate<class T,class Ret,class Ptr> // 三个模板参数

struct __list_iterator
{
  typedef __list_node<T> Node;
  typedef ____list_iterator<T,Ret,Ptr> Self;
 
  Node* _node ;//成员变量

  __list_iterator(Node* node) //构造函数
       :_node(node)
      {}

  Ref operator*()
  {
    return _node->_data;
  }

   Self& operator->()
  {
    return &_node->_date;
  }

   Node* _node; //成员变量
  
} 

1. operator*: 就是返回当前节点的数据

2 .operator->

让我们看一下以上代码,如果我们这样像“cout<<*it”这样写会发现编不过,因为我们(*it)之后会发现我们存储的数据并不是内置类型,而是自定义类型的数据所以“”cout<<“”便会报错,而我们却想要像指针一样依次读取的Date类里的元素就只能重载一下“->“。

但是我们会发现应该正确的写法是这样的   "it->"找到存储的数据  在使用"->"找到Date里面存储动的_year,等,而我们只需要使用一次"->"就可以实现,其实这是为了代码的可读性,编译器特殊处理了一下,不必在意。

3、前置++,后置++,前置--,后置--

tepmplate<class T,class Ret,class Ptr> // 三个模板参数

struct __list_iterator
{
  typedef __list_node<T> Node;
  typedef ____list_iterator<T,Ret,Ptr> Self;
 
  Node* _node ;//成员变量
  
  Self& operator++() //前置++
  {
     _node=node->_next; //指向下一个结点
     return *this;
  }

  Self operator++(int) //后置++
  {
     Self tmp=(*this) //定义一个指向自己结点的tmp变量
     ++(*this) //指向下一个结点
     return *this; //返回tmp变量
  }
  
  Self& oprator--() //前置--
  {
    _node=_node->_prev; //指向上一个结点
    return *this;
  }

  Self operator--(int) //后置--
  {
     Self tmp=(*this) //定义一个指向自己结点的tmp变量
     ++(*this) //指向上一个结点
     return *this; //返回tmp变量
  }
} 

"=="和"!="的重载

tepmplate<class T,class Ret,class Ptr> 

struct __list_iterator
{
  typedef __list_node<T> Node;
  typedef ____list_iterator<T,Ret,Ptr> Self;
 
  Node* _node ;//成员变量
  
  // it!=end()
  bool operator!=(const Self& it)
  {
    return _node !=it._node;  
  }
  
  //it==end()
  bool operator==(const Self& it)
  {
    return _node ==it._node;  
  }

} 

3、list的普通迭代器和const迭代器

list是一个带头结点的双向循环链表,

begin(),指向的是第一个结点,end(). 是最后一个结点的下一个结点

tmmplate<class T>
class list
{
  typedef __list_node<T> Node;
public:
  typedef __list_iterator<T,T&,T*>  iterator;  //普通迭代器
  
  typedef  __list_iterator<T,const T&,const T*>  const_iterator;  //const迭代器
  
  iterator begin()  //能读能写的正向迭代器
  {
    return iterator(_head->_next);
  }

  iterator end()
  {
    return iterator(_head);
  }

  const_iterator begin() const   //只能读的正向 const 迭代器
  {
    return iterator(_head->_next);
  }

  const_iterator end() const  
  {
    return iterator(_head);
  }

private:
Node* _head; //成员变量
}

4.list的insert&erase

insert()就是在任意一个位置插入

void insert(iterator pos,const T& x)
{
  Node*cur=pos._node; //取到pos位置的结点
  Node* prev=cur->_prev;
  Node* newnode =new Node(x);

  //prev   nexnode   cur
  prev->_next=newnode;
  newnode->_prev=prev;
  newnode->_next=cur;
  cur->_prev=newnode;
}

erase就是任意位置删除

void erase(iterator pos,const T& x)
{
  assert(pos!=end())
  
  Node* cur=pos._node;
  Node* prev=cur->_prev;
  Node* next=cur->_next;
  delete cur; 
  
  prev->_next=next;
  next->_prev=prev;
}

5.list的push_back, pop_back   push_front  pop_front  clear

应为我们之前写过 insert 和 erase 我们这里的函数便可以直接调用

void push_back(const T& x) //尾插
{
  insert(end(),x);
}

void push_front(const T& x) //头插
{
  insert(begin(),x);
}

void pop_back() //尾删
{
  erase(--end());
}

void pop_front() //头删
{
  erase(begin())
}

void clear() //清除除头结点外所有节点
{
  iterator it=begin();
  while(it!=end())
  {
    erase(it++);
  }
}

6.list的构造函数,析构函数,拷贝和赋值

构造函数将链表构造一个头结点,析构函数先用clear()将除头结点外的所以结点清除掉,在清除头结

template<class T>
class list
{
  typedef __list _node<T> Node;
public:
  typedef __list_iterator<T,T&,T*> iterator
  typedef __list_iterator<T,const T&,const T*> const_iterator

  list() //头结点双向循环链表
  {
    _head=mew Node;
    _head->_next=_head;
    _head->_prev=_head;
  }

  ~list()
  {
    clear();
    delete _head;
    _head=nullptr;
  }
  
private:
  Node* _head;
}

 拷贝构造和赋值

template<class T>
class list
{
  typedef __list _node<T> Node;
public:
  typedef __list_iterator<T,T&,T*> iterator
  typedef __list_iterator<T,const T&,const T*> const_iterator

 //lt2(lt)  //拷贝构造 
 list(cosnt list<T>& lt)
 {
    _head = new Node;
	_head->_next = _head;
	_head->_prev = _head;

   for(auto e: lt)
   {
     push_back(e);
   }
 }

 //lt1=lt3
 operator=(list<T> lt)  //传值,只是一份临时拷贝
 {
   swap(_head,lt._head); //交换头结点;
   return *this;
 }
  
private:
  Node* _head;
}

文章尚有不足,欢迎大牛指正

如果本文对您有帮助请点赞支持一下~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值