List 模拟实现

前言

本文将会向你介绍如何模拟实现list、iterator迭代器

模拟实现

引入

迭代器是一种用于访问容器中元素的对象,它封装了对容器中元素的访问方式。迭代器提供了一组操作接口,可以让我们通过迭代器对象来遍历容器中的元素。(iterator迭代器内部成员变量和成员函数都是与节点有关的)

先举一个例子,以下是迭代器++成员函数的实现
Self看不懂没关系,我们现在只要知道它是迭代器类名typedef过来的就好了,_pNode是一个指向ListNode结构体的指针。在ListIterator类中,_pNode用于指向链表中的某个节点,通过该指针可以访问节点的成员变量和成员函数。本质上还是对节点做操作,那我们为什么要大费周章还要套一个迭代器类呢?为什么不能大大方方的直接使用节点指针呢?
原因是:虽然写的人麻烦了,但却能方便使用者,使用者直接对迭代器对象++就能完成遍历

我们接下来就要做这个事情了。

        Self& operator++()
        {
            _pNode = _pNode->_pNext;    //让节点指针指向下一个节点    
            //返回迭代器对象
            return *this;
        }
迭代器对象和指向节点的指针之间有以下关系: 迭代器对象封装了指向节点的指针,提供了一组操作接口,使得我们可以通过迭代器对象来访问节点中的元素。 迭代器对象可以通过指针的方式来访问节点中的元素,可以使用指针运算符来获取元素的值,或者使用指针递增/递减来移动到下一个/上一个节点。 迭代器对象可以隐藏底层容器的具体实现细节,使得我们可以通过统一的接口来访问不同类型的容器,提高了代码的可复用性和灵活性。

整体结构

首先先让你熟悉一下整个结构

	//节点类
    template<class T>
    struct ListNode
    {
    
    }
    //迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
		  //...
		  //利用结构体指针对节点结构体进行管理
	      PNode _pNode;   //成员变量:结构体指针
     };
	//list类
    template<class T>
    class list
    {
		
		private:		
        PNode _pHead;	//哨兵位头节点(结构体指针)
        size_t _size;	//节点个数
    }

节点类

定义了一个节点结构体,在链表类中,我们需要能够直接访问节点的指针 _pPre 和 _pNext,以及节点的值 _val。如果将节点类定义为class,则需要在节点类中将这些成员变量和成员函数声明为public,或者在链表类中添加友元关系,以便链表类可以访问和操作节点的私有成员。
  template<class T>
  struct ListNode
  {
      //构造函数
      ListNode(const T& val = T())
          : _val(val) //节点的值
          , _pPre(nullptr)
          , _pNext(nullptr)
          {}
      ListNode<T>* _pPre; //节点的前驱
      ListNode<T>* _pNext; //节点的后继
      T _val;
  };

迭代器类

定义一个ListIterator类,在这个迭代器类中使用节点的指针,底层还是使用节点指针,迭代器类可以看作对节点类的封装,它提供了对节点的操作和访问的接口,使得用户可以通过迭代器来遍历和操作链表中的节点。 这里有一个点需要注意一下 这里的Ref指的是reference(引用), Ptr指的是pointer(指针),当然这里用T&、T*替换Ref和Ptr也是可以的 我们可以看到实例化部分,T&就传给了Ref,T * 就传给了Ptr

在这里插入图片描述
在这里插入图片描述
那么我们为什么要多传递两个模板参数呢?原因这两个模板参数可以助我们区分const迭代器类和普通迭代器类
在这里插入图片描述

值得一提的是重载->
当我们定义一个AA类型的对象的时候,并传入二元的数据,但是<<是不支持的(cout << *it << endl;) 我们可以想到用 cout << (*it)._a1 << " " << (*it)._a2 << endl, 这当然可以
但是迭代器模拟的是指针的行为,用->似乎更恰当一些,于是我们就重载了->运算符
operator->() 返回了 &_pNode->_val,这是什么意思呢?
&_pNode->_val这是一个指向AA类型数据的指针(AA *),对象指针再加上一个->就可以访问成员变量啦
cout << it.operator->() ->_a1 << " " << it.operator->()->_a2 << endl;

    struct AA
    {
        AA(int a1 = 0, int a2 = 0)
            :_a1(a1)
            ,_a2(a2)
        {}
        int _a1;
        int _a2;
    };
    void test_list4()
    {
        Fan::list<AA> lt;
        lt.push_back(AA(1, 1));
        lt.push_back(AA(2, 2));
        lt.push_back(AA(3, 3));
        Fan::list<AA>::iterator it = lt.begin();
        while (it != lt.end())
        {
            //AA对象不支持流插入
          	//error:cout << *it << endl;
            //cout << (*it)._a1 << " " << (*it)._a2 << endl;
            cout << it->_a1 << " " << it->_a2 << endl;
            //cout << it.operator->() ->_a1 << " " << it.operator->()->_a2 << endl;
            it++;
        }
        cout << endl;
    }
    ```

 ```c
 template<class T, class Ref, class Ptr>
    class ListIterator
    {
        //typedef结点类
        typedef ListNode<T>* PNode;
        //typedef迭代器类
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        //构造函数
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode) //初始化指向节点的指针
        {}
        //拷贝构造函数
        //l2(l1)
        ListIterator(const Self& l)
        {
            _pNode = l._pNode;  
        }
        //重载*
        //从泛型的角度来说我们并不知道operator*()的返回值是什么
        Ref operator*()
        {
            return _pNode->_val;   //节点指针指向的值
        }
        //重载->(为自定义类型的数据准备)
        Ptr operator->()
        {
            return &_pNode->_val;   
        }
        //重载++
        Self& operator++()
        {
            _pNode = _pNode->_pNext;    //让节点指针指向下一个节点    
            //返回迭代器对象
            return *this;
        }
        //重载后置++
        Self operator++(int)
        {
            Self tmp(*this);    //保存当前迭代器对象(当前指针指向的节点)
            _pNode = _pNode->_pNext;    
            return tmp;         //返回修改前的迭代器对象
        }
        //重载--
        Self& operator--()
        {
            _pNode = _pNode->_pPre; //让节点指针指向前一个节点
            //返回迭代器对象
            return *this;
        }
        //重载后置--
        Self& operator--(int)
        {
            Self tmp(*this);    //保存当前迭代器对象(当前指针指向的节点)
            _pNode = _pNode->_pPre;
            return tmp;         //返回修改前的迭代器对象
        }
        //重载!=
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;  //判断节点指针是否相等
        }
        //重载==
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;  //判断节点指针是否相等
        }
        PNode _pNode;   //成员变量:节点指针
    };

list类

实例化类模板

用typedef重命名,隐藏底层的细节,提供统一的访问修改方式

 //list类
    template<class T>
    class list
    {
    	//实例化节点类模板
        typedef ListNode<T> Node;   
        typedef Node* PNode;        //重命名Node*指针类型
    public:
    	//实例化类模板
        typedef ListIterator<T, T&, T*> iterator;   
        //实例化const版本
        typedef ListIterator<T, const T&, const T&> const_iterator;
默认成员函数
// List的构造
        list()
        {
        	//初始化头节点
            CreateHead();
        }
        //n个value的构造
        list(int n, const T& value = T())
        {
            CreateHead();  
            while (n--)
            {
                push_back(value);   //尾插
            }
        }
        template <class Iterator>
        //一段迭代器空间的构造
        list(Iterator first, Iterator last)
        {
            CreateHead(); 
            while (first != last)
            {
                push_back(*first);  //尾插    
                first++;
            }
        }
        //拷贝构造
        //lt2(lt1)
        list(const list<T>& l)
        {
            CreateHead();   
            for (auto e : l)
            {
                push_back(e);
            }
        }
        //赋值重载
        //lt2 = lt1
        list<T>& operator=(list<T> l)
        {
            clear();    //清除数据(不包括头节点)
            for (auto e : l)
            {
                push_back(e);
            }
            return *this;
        }
        //析构函数
        ~list()
        {
            clear();
            //释放头节点
            delete _pHead;
            _pHead = nullptr;
        }
获取指向头尾节点的指针
 // List Iterator
        //返回第一个节点的指针(不是头节点)
        iterator begin()
        {
            //return iterator(_pHead->_pNext);
            return _pHead->_pNext;
        }
        //返回最后一个节点的下一个位置的节点指针
        iterator end()
        {
            //return iterator(_pHead)
            return _pHead;
        }
        //const:返回第一个节点的指针(不是头节点)
        const_iterator begin()  const
        {
            return const_iterator(_pHead->_pNext);
            //return _pHead->_pNext;  
        }
        //const://返回最后一个节点的下一个位置的节点指针
        const_iterator end()  const
        {
            return const_iterator(_pHead);
                //return _pHead;
        }
容量
        // List Capacity
        size_t size()
        {
            return _size;
        }
        bool empty()const
        {
            return _size == 0;
        }
获取头尾结点的值

        //返回第一个节点的值(不是头节点)
        T& front()
        {
            return _pHead->_pNext->_val;
        }
        //const版本:返回第一个节点的值(不是头节点)
        const T& front()const
        {
            return _pHead->_pNext->_val;
        }
        //返回最后一个节点的值(注意与end()区别)
        T& back()
        {
            return _pHead->_pPre->_val;
        }
        //const版本:返回最后一个节点的值
        const T& back()const
        {
            return _pHead->_pPre->_val;
        }
修改
  // List Modify
        void push_back(const T& val)
        {
            insert(end(), val); //复用
        }
        void pop_back()
        {
            erase(--end()); //复用
        }
        void push_front(const T& val)
        {
            insert(begin(), val); //复用
        }
        void pop_front()
        {
            erase(begin()); //复用
        }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            ++_size;
            //结构体指针
            PNode cur = pos._pNode;
            PNode prev = cur->_pPre;

            PNode newnode = new Node(val);
            prev->_pNext = newnode;
            cur->_pPre = newnode;
            newnode->_pPre = prev;
            newnode->_pNext = cur;
            //返回新节点位置处的迭代器
            return iterator(newnode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            --_size;
            PNode cur = pos._pNode;
            PNode prev = cur->_pPre;
            PNode next = cur->_pNext;

            prev->_pNext = next;
            next->_pPre = prev;
            delete cur;
            return iterator(next);
        }

测试

void test_list1()
{
   Fan::list<int> lt;
   lt.push_back(1);
   lt.push_back(2);
   lt.push_back(3);
   lt.push_back(4);
   lt.push_back(5);
   lt.push_back(6);
   Fan::list<int>::iterator it = lt.begin();
   while (it != lt.end())
   {
       cout << *it << " ";
       ++it;
   }
   cout << endl;
   for (auto e : lt)
   {
       cout << e << " ";
   }
   cout << endl;
}
void test_list2()
{
   Fan::list<int> lt1;
   lt1.push_back(1);
   lt1.push_back(2);
   lt1.push_back(3);
   lt1.push_back(4);
   lt1.push_back(5);
   Fan::list<int>::iterator it1 = lt1.begin();
   while (it1 != lt1.end())
   {
       cout << *it1 << " ";
       ++it1;
   }
   cout << endl;
   Fan::list<int> lt2(lt1.begin(), lt1.end());
   Fan::list<int>::iterator it2 = lt2.begin();
   while (it2 != lt2.end())
   {
       cout << *it2 << " ";
       ++it2;
   }
   cout << endl;
   Fan::list<int> lt3(lt2);
   Fan::list<int>::iterator it3 = lt3.begin();
   while (it3 != lt3.end())
   {
       cout << *it3 << " ";
       ++it3;
   }
}
//打印const对象
   void print_list(const Fan::list<int>& lt)
   {
       Fan::list<int>::const_iterator it = lt.begin();
       while (it != lt.end())
       {
           cout << *it << " ";
           ++it;
       }
       cout << endl;
       for (auto e : lt)
       {
           cout << e << " ";
       }
   }
   void test_list3()
   {
       Fan::list<int> lt;
       lt.push_back(1);
       lt.push_back(2);
       lt.push_back(3);
       lt.push_back(4);
       lt.push_back(5);
       lt.push_back(6);
       print_list(lt);
   }

小结

本篇文章对于初步接触模板的朋友们是有一定难度的,看完这篇文章,希望你能有所收获,在对模板的理解上更进一步,如果本文存在疏漏或错误的地方,还请您能够指出!

posted @ 2023-10-14 23:48  Fan_558  阅读(3)  评论(0编辑  收藏  举报  来源