21. C++STL 7(8000字详解list及其迭代器的模拟实现)

打印 上一主题 下一主题

主题 834|帖子 834|积分 2502

⭐本篇重点:STL中的list及其迭代器的模拟实现和测试
  ⭐本篇代码:c++学习 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)
  目次
一. list的节点
二. list的迭代器 
2.1 迭代器框架
 2.2 迭代器实现
三. list的实现
3.1 list的构造函数
3.2 insert
3.2 erase 
3.3 begin和end
3.4 push_back和push_front
3.5 pop_back和pop_front
3.6 clear和析构函数
3.7 测试代码1
3.8 拷贝构造函数和赋值运算符重载 
四. test.h 源代码
五. 测试自界说类型和类类型
5.1测试string类
5.2 测试自界说类
六. 下篇重点:stack和queue的使用与模拟实现


一. list的节点

        我们知道,list是一个带头的双向循环链表。所以这个节点应该包含以下成员变量。
  1.     //表示链表的节点
  2.     template<class T>
  3.         struct ListNode
  4.         {
  5.                 ListNode* _next;
  6.                 ListNode* _prev;
  7.                 T _data;
  8.         //构造函数
  9.                 ListNode(const T& data = T())
  10.                         :_next(nullptr)
  11.                         , _prev(nullptr)
  12.                         , _data(data)
  13.                 {};
  14.         };
复制代码
二. list的迭代器 

        我们模拟过string和vector,它两的迭代器都用原生指针就能实现。但是list的迭代器使用原生指针无法实现。好比我们++it,不能简朴的让指针+1就行。
2.1 迭代器框架

        list迭代器结构体如下:
  1.         //迭代器,T为节点的data,Ptr表示data的地址,Ref表示data的引用
  2.         template<class T, class Ptr, class Ref>
  3.         struct ListIterator
  4.         {
  5.                 typedef ListNode<T> Node;
  6.                 typedef ListIterator<T, Ptr, Ref> Self;
  7.                 Node* _node;
  8.                 //构造函数
  9.                 ListIterator(Node* node)
  10.                         :_node(node)
  11.                 {}
  12.                 //前置++
  13.                 Self& operator++()
  14.                 {}
  15.                 //后置++
  16.                 Self operator++(int)
  17.                 {}
  18.                 //前置--
  19.                 Self& operator--()
  20.                 {}
  21.                 //后置--
  22.                 Self operator--(int)
  23.                 {}
  24.                 //解引用,获取这个节点
  25.                 Ref operator*()
  26.                 {}
  27.                 //->,箭头获取的是节点的指针
  28.                 Ptr operator->()
  29.                 {}
  30.                 //判断是否相等
  31.                 bool operator==(const Self& self)
  32.                 {}
  33.                 //判断是否不等
  34.                 bool operator!=(const Self& self)
  35.                 {}
  36.         };
复制代码
 2.2 迭代器实现

operator++
在list中,++只必要让我们的指针指向当前节点的下一个节点即可
前置++
  1.                 //前置++
  2.                 Self& operator++()
  3.                 {       
  4.                         //返回++后的结果
  5.                         _node = _node->_next;
  6.                         return *this;       
  7.                 }
复制代码
后置++。后置++留意要用中心变量生存并且不能引用返回!
  1.                 //后置++
  2.                 Self operator++(int)
  3.                 {
  4.                         //返回++前的结果,需要保存++前的指针
  5.                         //注意这里不可使用引用返回,tmp在栈中。属于局部变量,出函数会销毁!
  6.                         Node* tmp = _node;
  7.                         _node = _node->_next;
  8.                         return tmp;
  9.                 }
复制代码
operator* 和 operator->
*返回当前节点的data,->返回当前节点data的地址(即一个指针) 
好比: *it = data         it-> = &data
  1.                 //解引用,获取这个节点
  2.                 Ref operator*()
  3.                 {
  4.                         return _node->_data;
  5.                 }
  6.                 //->,箭头获取的是节点的指针
  7.                 Ptr operator->()
  8.                 {
  9.                         return &_node->_data;
  10.                 }
复制代码
根据上面的代码        --和== !=同理可以实现 
迭代器的全部实现代码如下:
  1.         //迭代器,T为节点的data,Ptr表示data的地址,Ref表示data的引用        template<class T, class Ptr, class Ref>        struct ListIterator        {                typedef ListNode<T> Node;                typedef ListIterator<T, Ptr, Ref> Self;                Node* _node;                //构造函数                ListIterator(Node* node)                        :_node(node)                {}                //前置++
  2.                 Self& operator++()
  3.                 {       
  4.                         //返回++后的结果
  5.                         _node = _node->_next;
  6.                         return *this;       
  7.                 }                //后置++
  8.                 Self operator++(int)
  9.                 {
  10.                         //返回++前的结果,需要保存++前的指针
  11.                         //注意这里不可使用引用返回,tmp在栈中。属于局部变量,出函数会销毁!
  12.                         Node* tmp = _node;
  13.                         _node = _node->_next;
  14.                         return tmp;
  15.                 }                //前置--                Self& operator--()                {                        _node = _node->_prev;                        return *this;                }                //后置--                Self operator--(int)                {                        Node* tmp = _node;                        _node = _node->_prev;                        return tmp;                }                //解引用,获取这个节点
  16.                 Ref operator*()
  17.                 {
  18.                         return _node->_data;
  19.                 }
  20.                 //->,箭头获取的是节点的指针
  21.                 Ptr operator->()
  22.                 {
  23.                         return &_node->_data;
  24.                 }                //判断是否相等                bool operator==(const Self& it)                {                        return it._node == _node;                }                //判断是否不等                bool operator!=(const Self& it)                {                        return it._node != _node;                }        };
复制代码
三. list的实现

list的框架如下。位于 test.h中
  1.         template<class T>
  2.         class list
  3.         {
  4.                 typedef ListNode<T> Node;        //typedef list的节点
  5.         public:
  6.                 //list 的迭代器
  7.                 typedef ListIterator<T, T*, T&> iterator;
  8.                 typedef ListIterator<T, const T*, const T&> const_iterator;
  9.         private:
  10.                 Node* _head;        //list的头节点
  11.         };
复制代码
3.1 list的构造函数

         list是带头双向循环链表。只要在堆中开辟一个头节点,然后让它的next和prev都指向自己即可。留意头节点的data不存储任何值。
  1.                 //构造函数
  2.                 list()
  3.                         :_head(new Node)
  4.                 {
  5.                         _head->_next = _head;
  6.                         _head->_prev = _head;
  7.                 }
复制代码
3.2 insert

        和vector一样,我们先界说出insert和erase。然后push_back和pop_back去复用insert和erase的代码可以提高代码的复用。
        思考一下list的insert中的pos是什么?        list没有下标,只能用迭代器表示pos
插入代码的逻辑图如下

我们只要让prev链接号newnode,再让newnode链接好cur即可 (留意提前生存好cur)
代码如下:
  1.                 //insert。在pos位置插入data
  2.                 void insert(const iterator& pos, const T& data)
  3.                 {
  4.                         Node* newnode = new Node(data);
  5.                         Node* cur = pos._node;
  6.                         Node* pre = cur->_prev;
  7.                         //1.链接pre和newnode
  8.                         pre->_next = newnode;
  9.                         newnode->_prev = pre;
  10.                         //2.链接newnode和cur
  11.                         newnode->_next = cur;
  12.                         cur->_prev = newnode;
  13.                 }
复制代码
即便只有一个头节点上面代码也没问题。逻辑图如下 

3.2 erase 

        erase比较简朴。找到pos的前后节点pre和next,链接pre和next,然后删除cur即可
留意:返回的节点应该是next节点(即删除cur后,next处于pos的位置)
代码如下:
  1.         //erase,删除pos位置的节点
  2.                 iterator erase(const iterator& pos)
  3.                 {
  4.                         Node* cur = pos._node;
  5.                         Node* pre = cur->_prev;
  6.                         Node* next = cur->_next;
  7.                         pre->_next = next;
  8.                         next->_prev = pre;
  9.                         delete cur;
  10.                         return next;        //删除cur后,pos就处于next了
  11.                 }
复制代码
3.3 begin和end

        begin返回第一个节点(头节点的next)的迭代器,end末了一个节点后一个的迭代器(就是头节点)
代码如下:
  1.                 iterator begin()
  2.                 {
  3.                         return _head->_next;
  4.                 }
  5.                 iterator end()
  6.                 {
  7.                         return _head;
  8.                 }
  9.                 const_iterator begin()const
  10.                 {
  11.                         return const_iterator(_head->_next);
  12.                 }
  13.                 const_iterator end()const
  14.                 {
  15.                         return const_iterator(_head);
  16.                 }
复制代码
3.4 push_back和push_front

        有了insert这两个函数就简朴了。push_back直接在end()这个迭代器调用insert,push_front直接在begin()调用insert。
代码如下:
  1.                 //尾插
  2.                 void push_back(const T& data)
  3.                 {
  4.                         insert(end(), data);
  5.                 }
  6.                 //头插
  7.                 void push_front(const T& data)
  8.                 {
  9.                         insert(begin(), data);
  10.                 }
复制代码
3.5 pop_back和pop_front

同理。调用erase即可。不外留意尾删是删除末了一个节点不是头节点!
  1.                 //头删
  2.                 void pop_front()
  3.                 {
  4.                         erase(begin());
  5.                 }
  6.                 //尾删
  7.                 void pop_back()
  8.                 {
  9.                         //erase(end());                        //不是删除头节点,而是头节点的前一个节点(尾节点)
  10.                         erase(_head->_prev);
  11.                 }
复制代码
3.6 clear和析构函数

        使用迭代器遍历链表,一个一个删除节点即可。clear不会删除头节点
  1.                 //clear清空链表
  2.                 void clear()
  3.                 {
  4.                         iterator it = begin();
  5.                         while (it != end())
  6.                         {
  7.                                 erase(it++);        //后置++,it走到下一个节点后。返回前一个节点去删除即可
  8.                         }
  9.                 }
复制代码
        析构函数。调用clear然后删除头节点即可。
  1.                 ~list()
  2.                 {
  3.                         clear();
  4.                         delete _head;
  5.                         _head = nullptr;
  6.                 }
复制代码
3.7 测试代码1

        到此为止,整个链表基本实现了。我们来测试一下
测试代码 test.cpp
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. #include "test.h"
  4. using namespace std;
  5. void test1()
  6. {
  7.         YZC::list<int> l1;
  8.         for (int i = 0; i < 5; i++)                // 0 1 2 3 4
  9.                 l1.push_back(i);
  10.        
  11.         for (int i = 10; i < 15; i++)        // 14 13 12 11 10 0 1 2 3 4
  12.                 l1.push_front(i);       
  13.         l1.pop_back();        // 14 13 12 11 10 0 1 2 3
  14.         l1.pop_front();        // 13 12 11 10 0 1 2 3
  15.         YZC::list<int>::iterator it = l1.begin();
  16.         while (it != l1.end())
  17.         {
  18.                 cout << *it << " ";
  19.                 ++it;
  20.         }
  21.         cout << endl;
  22. }
  23. int main()
  24. {
  25.         test1();
  26.         return 0;
  27. }
复制代码
 运行结果如下

3.8 拷贝构造函数和赋值运算符重载 

        拷贝构造。遍历构造即可
  1.                 //拷贝构造
  2.                 list(const list<T>& l)
  3.                         :_head(new Node)
  4.                 {
  5.                         //1.初始化头节点
  6.                         _head->_next = _head;
  7.                         _head->_prev = _head;
  8.                         //2.遍历l,尾插节点即可
  9.                         for (auto const& e : l)
  10.                         {
  11.                                 push_back(e);
  12.                         }
  13.                 }
复制代码
        赋值运算符重载。使用暂时变量出函数销毁的特性 
  1.                 //operator=
  2.                 list& operator=(list<T> l)
  3.                 {
  4.                         swap(_head, l._head);
  5.                         return *this;
  6.                 }
复制代码
四. test.h 源代码

  1. #pragma oncenamespace YZC{        //表示链表的节点        template<class T>        struct ListNode        {                ListNode* _next;                ListNode* _prev;                T _data;                ListNode(const T& data = T())                        :_next(nullptr)                        , _prev(nullptr)                        , _data(data)                {};        };        //迭代器,T为节点的data,Ptr表示data的地址,Ref表示data的引用        template<class T, class Ptr, class Ref>        struct ListIterator        {                typedef ListNode<T> Node;                typedef ListIterator<T, Ptr, Ref> Self;                Node* _node;                //构造函数                ListIterator(Node* node)                        :_node(node)                {}                //前置++
  2.                 Self& operator++()
  3.                 {       
  4.                         //返回++后的结果
  5.                         _node = _node->_next;
  6.                         return *this;       
  7.                 }                //后置++
  8.                 Self operator++(int)
  9.                 {
  10.                         //返回++前的结果,需要保存++前的指针
  11.                         //注意这里不可使用引用返回,tmp在栈中。属于局部变量,出函数会销毁!
  12.                         Node* tmp = _node;
  13.                         _node = _node->_next;
  14.                         return tmp;
  15.                 }                //前置--                Self& operator--()                {                        _node = _node->_prev;                        return *this;                }                //后置--                Self operator--(int)                {                        Node* tmp = _node;                        _node = _node->_prev;                        return tmp;                }                //解引用,获取这个节点
  16.                 Ref operator*()
  17.                 {
  18.                         return _node->_data;
  19.                 }
  20.                 //->,箭头获取的是节点的指针
  21.                 Ptr operator->()
  22.                 {
  23.                         return &_node->_data;
  24.                 }                //判断是否相等                bool operator==(const Self& it)                {                        return it._node == _node;                }                //判断是否不等                bool operator!=(const Self& it)                {                        return it._node != _node;                }        };        template<class T>        class list        {                typedef ListNode<T> Node;        //typedef list的节点        public:                //list 的迭代器                typedef ListIterator<T, T*, T&> iterator;                typedef ListIterator<T, const T*, const T&> const_iterator;                //构造函数
  25.                 list()
  26.                         :_head(new Node)
  27.                 {
  28.                         _head->_next = _head;
  29.                         _head->_prev = _head;
  30.                 }                //拷贝构造
  31.                 list(const list<T>& l)
  32.                         :_head(new Node)
  33.                 {
  34.                         //1.初始化头节点
  35.                         _head->_next = _head;
  36.                         _head->_prev = _head;
  37.                         //2.遍历l,尾插节点即可
  38.                         for (auto const& e : l)
  39.                         {
  40.                                 push_back(e);
  41.                         }
  42.                 }                //operator=
  43.                 list& operator=(list<T> l)
  44.                 {
  45.                         swap(_head, l._head);
  46.                         return *this;
  47.                 }                                //析构函数                ~list()
  48.                 {
  49.                         clear();
  50.                         delete _head;
  51.                         _head = nullptr;
  52.                 }                //clear清空链表
  53.                 void clear()
  54.                 {
  55.                         iterator it = begin();
  56.                         while (it != end())
  57.                         {
  58.                                 erase(it++);        //后置++,it走到下一个节点后。返回前一个节点去删除即可
  59.                         }
  60.                 }                //insert。在pos位置插入data
  61.                 void insert(const iterator& pos, const T& data)
  62.                 {
  63.                         Node* newnode = new Node(data);
  64.                         Node* cur = pos._node;
  65.                         Node* pre = cur->_prev;
  66.                         //1.链接pre和newnode
  67.                         pre->_next = newnode;
  68.                         newnode->_prev = pre;
  69.                         //2.链接newnode和cur
  70.                         newnode->_next = cur;
  71.                         cur->_prev = newnode;
  72.                 }                //erase,删除pos位置的节点                iterator erase(const iterator& pos)                {                        Node* cur = pos._node;                        Node* pre = cur->_prev;                        Node* next = cur->_next;                        pre->_next = next;                        next->_prev = pre;                        delete cur;                        return next;        //删除cur后,pos就处于next了                }                //尾插
  73.                 void push_back(const T& data)
  74.                 {
  75.                         insert(end(), data);
  76.                 }
  77.                 //头插
  78.                 void push_front(const T& data)
  79.                 {
  80.                         insert(begin(), data);
  81.                 }                 //头删
  82.                 void pop_front()
  83.                 {
  84.                         erase(begin());
  85.                 }
  86.                 //尾删
  87.                 void pop_back()
  88.                 {
  89.                         //erase(end());                        //不是删除头节点,而是头节点的前一个节点(尾节点)
  90.                         erase(_head->_prev);
  91.                 }                iterator begin()
  92.                 {
  93.                         return _head->_next;
  94.                 }
  95.                 iterator end()
  96.                 {
  97.                         return _head;
  98.                 }
  99.                 const_iterator begin()const
  100.                 {
  101.                         return const_iterator(_head->_next);
  102.                 }
  103.                 const_iterator end()const
  104.                 {
  105.                         return const_iterator(_head);
  106.                 }        private:                Node* _head;        //list的头节点        };}
复制代码
五. 测试自界说类型和类类型

5.1测试string类

测试代码和结果如下:
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. #include <string>
  4. #include "test.h"
  5. using namespace std;
  6. void test1()
  7. {
  8.         YZC::list<string> l1;
  9.         string s1 = "hello world!";
  10.         string s2 = "YZC";
  11.         string s3 = "yzc";
  12.         string s4 = "list模拟实现";
  13.         l1.push_back(s1);
  14.         l1.push_back(s2);
  15.         l1.push_back(s3);
  16.         l1.push_back(s4);
  17.         for (const auto& str : l1)
  18.                 cout << str << " ";
  19.         cout << endl;
  20.         //拷贝构造
  21.         YZC::list<string> l2(l1);
  22.         for (const auto& str : l2)
  23.                 cout << str << " ";
  24. }
  25. int main()
  26. {
  27.         test1();
  28.         return 0;
  29. }
复制代码

5.2 测试自界说类

 
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. #include "test.h"
  4. using namespace std;
  5. struct Date
  6. {
  7.         int _year = 0;
  8.         int _month = 1;
  9.         int _day = 1;
  10. };
  11. void test1()
  12. {
  13.         YZC::list<Date> l1;
  14.         Date d1;
  15.         Date d2;
  16.         l1.push_back(d1);
  17.         l1.push_back(d2);
  18.         //1 测试迭代器的解引用
  19.         auto it = l1.begin();
  20.         while (it != l1.end())
  21.         {
  22.                 cout << (*it)._year << "/" << (*it)._month << "/" << (*it)._day << endl;
  23.                 ++it;
  24.         }
  25.         cout << endl;
  26.         //测试赋值运算符和->
  27.         YZC::list<Date>l2 = l1;
  28.         auto it1 = l2.begin();
  29.         while (it1 != l2.end())
  30.         {
  31.                 cout << it->_year << "/" << it->_month << "/" << it->_day << endl;
  32.                 ++it1;
  33.         }
  34. }
  35. int main()
  36. {
  37.         test1();
  38.         return 0;
  39. }
复制代码
测试结果如下:

六. 下篇重点:stack和queue的使用与模拟实现


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

老婆出轨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表