C++:模仿实现list

打印 上一主题 下一主题

主题 803|帖子 803|积分 2409

目次

节点
迭代器
团体框架
构造函数
empty_init
拷贝构造
赋值重载
析构函数
clear
insert
erase
push_back和push_front
pop_back和push_front
size
empty
Print_Container

节点

对于链表节点,我们须要一个数据、一个前驱指针、一个后继指针来维护,而且将其封装成一个类。
  1. template<class T>
  2. struct list_node
  3. {
  4.         T _data;
  5.         list_node<T>* _next;
  6.         list_node<T>* _prev;
  7.         list_node(const T& data = T())
  8.                 :_data(data)
  9.                 ,_next(nullptr)
  10.                 ,_prev(nullptr)
  11.         {}
  12. };
复制代码
使用struct的缘故原由是因为,struct默认的域作用限定符是public,方便后续使用,不消走友元的那一套。
迭代器

我们知道迭代器提供访问容器的方法,之前实现vector和string时,迭代器用的就是数据类型的指针,但是list不可以直接用。因为vector和string的数据在内存的存放都是连续的,如果想找下一个数据的指针(迭代器),直接(迭代器)指针++就可以了;但是list的数据存放在内存不是连续的,如果直接把指针当成迭代器,迭代器++是找不到下一个数据的迭代器
以是综上所述,我们应该用类对链表数据类型的指针封装成迭代器在类里重载利用符让其达到我们想要的效果。
当然,我们实现的迭代器应该有两个版本,普通版本和const版本。
  1. //普通迭代器
  2. template<class T>
  3. struct list_iterator
  4. {
  5.         typedef list_node<T> Node;
  6.         typedef list_iterator<T> Self;
  7.         Node* _node;
  8.         list_iterator(Node* node)
  9.                 :_node(node)
  10.         {}
  11.         T& operator*()
  12.         {
  13.                 return _node->_data;
  14.         }
  15.         T* operator->()
  16.         {
  17.                 return &_node->_data;
  18.         }
  19.         //前置++
  20.         Self& operator++()
  21.         {
  22.                 _node = _node->_next;
  23.                 return *this;
  24.         }
  25.         //后置++
  26.         Self operator++(int)
  27.         {
  28.                 Self tmp(*this);
  29.                 _node = _node->_next;
  30.                 return tmp;
  31.         }
  32.         //前置--
  33.         Self& operator--()
  34.         {
  35.                 _node = _node->_prev;
  36.                 return *this;
  37.         }
  38.         //后置--
  39.         Self operator--(int)
  40.         {
  41.                 Self tmp(*this);
  42.                 _node = _node->_prev;
  43.                 return tmp;
  44.         }
  45.         bool operator!=(const Self& it) const
  46.         {
  47.                 return _node != it._node;
  48.         }
  49.         bool operator==(const Self& it) const
  50.         {
  51.                 return _node == it._node;
  52.         }
  53. };
复制代码
  1. ​//const迭代器
  2. template<class T>
  3. struct list_const_iterator
  4. {
  5.         typedef list_node<T> Node;
  6.         typedef list_const_iterator<T> Self;
  7.         Node* _node;
  8.         list_const_iterator(Node* node)
  9.                 :_node(node)
  10.         {}
  11.         const T& operator*()
  12.         {
  13.                 return _node->_data;
  14.         }
  15.         const T* operator->()
  16.         {
  17.                 return &_node->_data;
  18.         }
  19.         Self& operator++()
  20.         {
  21.                 _node = _node->_next;
  22.                 return *this;
  23.         }
  24.         Self operator++(int)
  25.         {
  26.                 Self tmp(*this);
  27.                 _node = _node->_next;
  28.                 return tmp;
  29.         }
  30.         Self& operator--()
  31.         {
  32.                 _node = _node->_prev;
  33.                 return *this;
  34.         }
  35.         bool operator!=(const Self& it) const
  36.         {
  37.                 return _node != it._node;
  38.         }
  39.         bool operator==(const Self& it) const
  40.         {
  41.                 return _node == it._node;
  42.         }
  43. };
复制代码
我们发现这两份代码,除了重载*和->有所不同,其余代码都是一样的,以是我们可以增加两个模板参数,将这两份代码合二为一。
  1. template<class T, class Ref, class Ptr>
  2. struct list_iterator
  3. {
  4.         typedef list_node<T> Node;
  5.         typedef list_iterator<T, Ref, Ptr> Self;
  6.         Node* _node;
  7.         list_iterator(Node* node)
  8.                 :_node(node)
  9.         {}
  10.         Ref operator*()
  11.         {
  12.                 return _node->_data;
  13.         }
  14.         Ptr operator->()
  15.         {
  16.                 return &_node->_data;
  17.         }
  18.         Self& operator++() //前置++
  19.         {
  20.                 _node = _node->_next;
  21.                 return *this;
  22.         }
  23.         Self operator++(int) //
  24.         {
  25.                 Self tmp(*this);
  26.                 _node = _node->_next;
  27.                 return tmp;
  28.         }
  29.         Self& operator--()
  30.         {
  31.                 _node = _node->_prev;
  32.                 return *this;
  33.         }
  34.         Self operator--(int)
  35.         {
  36.                 Self tmp(*this);
  37.                 _node = _node->_prev;
  38.                 return tmp;
  39.         }
  40.         bool operator!=(const Self& it) const
  41.         {
  42.                 return _node != it._node;
  43.         }
  44.         bool operator==(const Self& it) const
  45.         {
  46.                 return _node == it._node;
  47.         }
  48. };
复制代码
 增加Ref和Ptr模板参数,让T*和T&作为参数传入,这就可以办理将两份代码合二为一。
团体框架

  1. template<class T>
  2. class list
  3. {
  4.         typedef list_node<T> Node;
  5. public:
  6.         /*typedef list_iterator<T> iterator;
  7.         typedef list_const_iterator<T> const_iterator;*/
  8.         //将T&和T*作为参数传入
  9.     typedef list_iterator<T, T&, T*> iterator;
  10.         typedef list_iterator<T, const T&, const T*> const_iterator;
  11.     iterator begin()
  12.     {
  13.             /*iterator it(_head->_next);
  14.             return it;*/
  15.             //return iterator(_head->_next);
  16.             //返回哨兵节点的下一个节点(第一个有效节点)
  17.             //隐式类型转换
  18.             return _head->_next;
  19.     }
  20.     iterator end()
  21.     {
  22.             //最后一个有效节点的下一位置,也就是哨兵节点
  23.             return _head;
  24.     }
  25.     const_iterator begin() const
  26.     {
  27.             return _head->_next;
  28.     }
  29.     const_iterator end() const
  30.     {
  31.             return _head;
  32.     }
  33.     //实现各种函数......
  34. private:
  35.         Node* _head;
  36.         size_t _size;
  37. };
复制代码
构造函数

empty_init

多种构造函数的代码都有重合,以是把重合部分独立成一个函数。
  1. void empty_init()
  2. {
  3.         //创造哨兵节点
  4.     _head = new Node();
  5.         _head->_next = _head;
  6.         _head->_prev = _head;
  7.         _size = 0;
  8. }
复制代码
普通构造
普通构造就是创造哨兵节点,调用empty_init即可。
  1. //普通构造
  2. list()
  3. {
  4.         empty_init();
  5. }
复制代码
列表构造
C++11的用法,用法例子如下:
  1. list<int> lt1 = { 1,2,3,4,5,6 };
复制代码
 先创造一个哨兵节点,然后将列表的元素尾插即可
  1. //列表构造
  2. list(initializer_list<T> il)
  3. {
  4.         empty_init();
  5.         for (auto& e : il)
  6.         {
  7.                 push_back(e);
  8.         }
  9. }
复制代码
关于列表initializer_list<T>的知识,可以看以下连接。
   介绍列表
  拷贝构造

创建哨兵节点,将链表元素尾插到待构造的链表就完成拷贝构造了。
  1. //拷贝构造
  2. list(const list<T>& lt)
  3. {
  4.         empty_init();
  5.         for (auto& e : lt)
  6.         {
  7.                 push_back(e);
  8.         }
  9. }
复制代码
赋值重载

暂时对象lt互换即可,跟string、vector的实现雷同。
  1. void swap(list<T>& lt)
  2. {
  3.         std::swap(_head, lt._head);
  4.         std::swap(_size, lt._size);
  5. }
  6. list<T>& operator=(list<T> lt)
  7. {
  8.         swap(lt);
  9.         return *this;
  10. }
复制代码
析构函数

clear

清理除了哨兵节点以外的所有节点。
  1. void clear()
  2. {
  3.         auto it = begin();
  4.         while (it != end())
  5.         {
  6.                 it = erase(it);
  7.         }
  8. }
复制代码
先将链表clear掉,然后清理哨兵节点。  
  1. ~list()
  2. {
  3.         clear();
  4.         delete _head;
  5.         _head = nullptr;
  6. }
复制代码
insert

在pos(迭代器)位置前插入元素x,插入后_size++,返回新插入元素的迭代器。
  1. iterator insert(iterator pos, const T& x)
  2. {
  3.         Node* cur = pos._node; //pos是iterator类的对象,访问里面的成员变量用pos._node,不能用pos->_node
  4.         Node* prev = cur->_prev;
  5.         Node* newnode = new Node(x);
  6.         newnode->_next = cur;
  7.         cur->_prev = newnode;
  8.         newnode->_prev = prev;
  9.         prev->_next = newnode;
  10.         ++_size;
  11.         //隐式类型转换
  12.         return newnode;
  13. }
复制代码
erase

删除pos位置的元素,删除后_size--,返回删除元素的下一元素的迭代器。
  1. iterator erase(iterator pos)
  2. {
  3.         assert(pos != end());          //不能删掉哨兵位的节点
  4.         Node* prev = pos._node->_prev;
  5.         Node* next = pos._node->_next;
  6.         prev->_next = next;
  7.         next->_prev = prev;
  8.         delete pos._node;
  9.         --_size;
  10.         return next;
  11. }
复制代码
push_back和push_front

利用insert函数就可以实现尾插和头插。
  1. void push_back(const T& x)
  2. {
  3.         /*Node* newnode = new Node(x);
  4.         Node* tail = _head->_prev;
  5.         tail->_next = newnode;
  6.         newnode->_prev = tail;
  7.         newnode->_next = _head;
  8.         _head->_prev = newnode;
  9.         ++_size;*/
  10.         insert(end(), x);
  11. }
  12. void push_front(const T& x)
  13. {
  14.         insert(begin(), x);
  15. }
复制代码
pop_back和push_front

利用erase函数实现尾删和头删。
  1. void pop_back()
  2. {
  3.         erase(--end());
  4. }
  5. void pop_front()
  6. {
  7.         erase(begin());
  8. }
复制代码
size

返回链表有效元素的个数.。
  1. size_t size() const
  2. {
  3.         return _size;
  4. }
复制代码
empty

判定链表是否为空。
  1. bool empty() const
  2. {
  3.         return _size == 0;
  4. }
复制代码
Print_Container

打印容器的函数。
  1. template<class Container>
  2. void Print_Container(const Container& con)
  3. {
  4.         //const对象要用const迭代器,这里没实现的话会报错
  5.         /*auto it = con.begin();
  6.         while (it != con.end())
  7.         {
  8.                 cout << *it << " ";
  9.                 ++it;
  10.         }*/
  11.         for (auto e : con)
  12.         {
  13.                 cout << e << " ";
  14.         }
  15.         cout << endl;
  16. }
复制代码

拜拜,下期再见
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

农妇山泉一亩田

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

标签云

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