当前位置:   article > 正文

【C++】-- STL之list模拟实现_仿照stl模板库中的list容器,制作一个属于自己的基本链表容器,实现以下功能

仿照stl模板库中的list容器,制作一个属于自己的基本链表容器,实现以下功能

目录

一、list模拟实现思路 

二、节点类的实现

三、list迭代器的实现

1._list_iterator类

2.构造函数

3.operator* 运算符重载

4.operator-> 运算符重载

5.operator!= 运算符重载

6.operator==运算符重载

7.前置++

8.后置++

9.前置--

10.后置--

四、list类的实现

1.list类 

2.构造函数

3.拷贝构造

4.赋值运算符重载

(1)传统的赋值运算符重载

(2)现代的赋值运算符重载

5.析构

6.迭代器

7.insert( ) 

8.erase( )

9.clear( )

10.push_front( )

11.push_back( )

12.pop_front( )

13.pop_back( )

14.empty( )

15 size( )

五、完整代码段


一、list模拟实现思路 

list链表的模拟实现,与前面的string和vector相比,略复杂一点:

(1)由于链表节点本身就是一个结构体,包含数据域、指针域。 将节点的相关操作放入节点类进行处理,逻辑更清晰

(2)string和vector的元素在空间上是连续分布的,迭代器++就能指向下一个元素,但list的迭代器不行,它的每个元素在空间上都不连续,要访问下一个节点必须找到当前节点的next,因此list的迭代器必须重写

链表主要模拟实现以下功能:

 

为了和库里面的list区分开,使用命名空间delia将 list类和库里list隔离开

  1. namespace delia
  2. {
  3. }

二、节点类的实现

节点类的成员变量有3个:

(1)节点值_val

(2)指向前一个节点的指针_prev

(3)指向后一个节点的指针_next 

节点无需拷贝构造、赋值运算符重载,由于没有额外申请空间,因此也不需要析构

  1. template<class T>
  2. struct _list_node
  3. {
  4. //3个成员变量
  5. T _val;
  6. _list_node<T>* _next;
  7. _list_node<T>* _prev;
  8. //构造函数
  9. _list_node(const T& val = T())
  10. :_val(val)
  11. , _prev(nullptr)
  12. , _next(nullptr)
  13. {};
  14. };

三、list迭代器的实现

由于节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符,如operator*、operator->、operator++等。用迭代器封装节点指针的思路不关心容器底层结构是数组、链表还是树等数据结构,都封装隐藏了底层细节,可以用一种统一的方式去访问、修改容器。

迭代器分为普通迭代器和const迭代器,对于_list_iterator类要实现两个版本,一个是普通的_list_iterator,另一个是const版本的_list_const_iterator,区别在于:对于两个类中的部分函数有普通函数和const函数之分(如begin( )和end( )),其他并无区别。因为这两个类的大部分代码相似,会造成代码冗余,如何解决呢?

对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。使用 

template<class T,class Ref,class Ptr>

类模板就会实例化出来两个类,一个是普通的不带const的T,T&, T*,另一个是带const的T,const T&, const T*,其中Ref是引用,Ptr是指针,该类模板实例化了以下这两个类模板:

  1. template _list_iterator<T, T&, T*> _iterator;
  2. template _list_iterator<T, const T&, const T*> const_iterator;

这就解决了需要写两个类的问题。 

1._list_iterator类

迭代链表的节点,只需要一个成员变量即节点 

  1. template<class T,class Ref,class Ptr>
  2. struct _list_iterator//使用_list_iterator类来封装node*
  3. {
  4. typedef _list_node<T> node;
  5. typedef _list_iterator<T,Ref,Ptr> self;
  6. node* _pnode;//成员变量
  7. }

2.构造函数

只需要初始化节点即可 

  1. //构造函数
  2. _list_iterator(node* pnode)
  3. :_pnode(pnode)
  4. {}

拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可

3.operator* 运算符重载

  1. //解引用,返回左值,是拷贝,因此要用传引用返回
  2. Ref operator*()
  3. {
  4. return _pnode->_val;
  5. }

4.operator-> 运算符重载

  1. //结构体指针解引用,返回节点值的指针
  2. Ptr operator->()
  3. {
  4. return &_pnode->_val;
  5. }

5.operator!= 运算符重载

  1. //!=重载
  2. bool operator!=(const self& s) const
  3. {
  4. return _pnode != s._pnode;
  5. }

6.operator==运算符重载

  1. //==重载
  2. bool operator==(const self& s) const
  3. {
  4. return _pnode == s._pnode;
  5. }

7.前置++

让迭代器走到下一个节点 

  1. //前置++ it.operator(&it)
  2. self& operator++()
  3. {
  4. _pnode = _pnode->next;
  5. return *this;
  6. }

8.后置++

  1. //后置++ 返回++之前的值 it.operator(&it,0)
  2. self operator++(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
  3. {
  4. self tmp(*this);
  5. _pnode = _pnode->_next;
  6. return tmp;
  7. }

9.前置--

让迭代器走到前一个节点 

  1. //前置-- it.operator(&it)
  2. self& operator--()
  3. {
  4. _pnode = _pnode->prev;
  5. return *this;
  6. }

10.后置--

  1. //后置-- 返回--之前的值 it.operator(&it,0)
  2. self operator--(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
  3. {
  4. self tmp(*this);
  5. _pnode = _pnode->_prev;
  6. return tmp;
  7. }

四、list类的实现

1.list类 

list的成员只需要一个头节点,通过迭代器访问其他节点元素

  1. template<class T>
  2. class list
  3. {
  4. typedef _list_node<T> node;
  5. public:
  6. typedef _list_iterator<T,T&,T*> iterator;//重命名迭代器
  7. typedef _list_iterator<T, const T&, const T*> const_iterator;//重命名const迭代器
  8. private:
  9. node* _head;
  10. };

2.构造函数

  1. list()
  2. {
  3. _head = new node;//会调_list_node的构造函数
  4. _head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
  5. _head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
  6. }

3.拷贝构造

创建一个空链表,再把参数链表节点的值一个一个尾插进去

  1. //拷贝构造 lt1(lt)
  2. list(const list<T>& lt)
  3. {
  4. //1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
  5. _head = new node;
  6. _head->_next = _head;
  7. _head->_prev = _head;
  8. //2.将lt的元素全部尾插到新链表
  9. for (const auto& e : lt)
  10. {
  11. push_back(e);
  12. }
  13. }

4.赋值运算符重载

(1)传统的赋值运算符重载

  1. //赋值运算符重载 lt1 = lt 传统写法
  2. list<T> operator=(const list<T>& lt)
  3. {
  4. //链表已存在,只需将节点尾插进去即可
  5. if(this != lt)
  6. {
  7. for (auto& e : lt)
  8. {
  9. push_back(e);
  10. }
  11. }
  12. }

(2)现代的赋值运算符重载

  1. list<T>& operator=(list<T>& lt)
  2. {
  3. swap(_head, lt->_head);
  4. return *this;
  5. }

其中,swap( )函数的执行过程如下:  

  1. template <class T> void swap ( T& a, T& b )
  2. {
  3. T c(a); a=b; b=c;
  4. }

在执行赋值运算符重载时,会调用拷贝构造函数执行深拷贝,然后再交换两个链表头节点的指针,把this要释放的资源交给临时对象,临时对象出了swap的函数作用域就被this要释放的资源也会被同时释放。

5.析构

(1)清除所有节点内容

(2)删除头节点,链表就销毁了 

  1. //析构
  2. ~list()
  3. {
  4. clear();//先清除所有节点内容
  5. delete _head;//再删除头节点
  6. _head = nullptr;
  7. }

6.迭代器

(1)普通迭代器

  1. iterator begin()
  2. {
  3. return iterator(_head->_next);//头节点不存数据
  4. }
  5. iterator end()
  6. {
  7. return iterator(_head);//尾节点的下一个节点位置即头节点
  8. }

 (2)const迭代器

  1. const_iterator begin() const
  2. {
  3. return const_iterator(_head->_next);//头节点不存数据
  4. }
  5. const_iterator end() const
  6. {
  7. return const_iterator(_head);//尾节点的下一个节点位置即头节点
  8. }

7.insert( ) 

(1)构造节点

(2)双向带头循环链表插入节点

  1. //插入节点
  2. void insert(iterator pos, const T& x)
  3. {
  4. assert(pos._pnode);
  5. node* newnode = new node(x);//构造节点
  6. node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
  7. //插入节点
  8. newnode->_next = pos._pnode;
  9. pos._pnode->_prev = newnode;
  10. prev->_next = newnode;
  11. newnode->_prev = prev;
  12. }

8.erase( )

(1)删除节点

(2)更新指针指向 

  1. //删除节点
  2. iterator erase(iterator pos)
  3. {
  4. assert(pos._pnode);//判断该位置节点是否存在
  5. assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
  6. node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
  7. node* next = pos._pnode->_next;//pos位置节点的后一个节点
  8. //删除节点
  9. delete pos._pnode;
  10. prev->_next = next;
  11. next->_prev = prev;
  12. return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
  13. }

9.clear( )

只清除所有节点内容,不能删除头节点,如果删除头节点那么链表就不存在了,是链表的析构函数需要完成的操作

  1. void clear()
  2. {
  3. iterator it = begin();
  4. while (it != end())
  5. {
  6. erase(it++);//erase需要传参迭代器,即位置
  7. }
  8. }

10.push_front( )

 头插

  1. //头插
  2. void push_front(const T& x)
  3. {
  4. insert(begin(), x);
  5. }

11.push_back( )

尾插

  1. //尾插
  2. void push_back(const T& x)
  3. {
  4. insert(end()--, x);
  5. }

12.pop_front( )

头删

  1. //头删
  2. void pop_front()
  3. {
  4. erase(begin());
  5. }

13.pop_back( )

尾删

  1. //尾删
  2. void pop_back()
  3. {
  4. erase(--end());
  5. }

14.empty( )

判空 

  1. //判空
  2. bool empty()
  3. {
  4. return end() == begin();
  5. }

15 size( )

求节点个数

  1. //求节点个数
  2. size_t size()
  3. {
  4. iterator it = begin();
  5. size_t sz = 0;
  6. while (it != end())//时间复杂度O(N)
  7. {
  8. it++;
  9. sz++;
  10. }
  11. return sz;
  12. }

五、完整代码段

016-list.h 

  1. #pragma once
  2. #include<iostream>
  3. #include<assert.h>
  4. using namespace std;
  5. namespace delia
  6. {
  7. template<class T>
  8. struct _list_node
  9. {
  10. T _val;
  11. _list_node<T>* _prev;
  12. _list_node<T>* _next;
  13. _list_node(const T& val = T())
  14. :_val(val)
  15. , _prev(nullptr)
  16. , _next(nullptr)
  17. {};
  18. };
  19. //节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符来控制
  20. //对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。
  21. //使用template<class T,class Ref,class Ptr>类模板就会实例化出来两个类,一个是T,T&, T*的,一个是T,const T&, const T*的
  22. //template _list_iterator<T, T&, T*> _iterator;
  23. //template _list_iterator<T, const T&, const T*> const_iterator;
  24. //这样就不需要再去定义一个如下const的迭代器类:
  25. //template<class T>
  26. //struct _list_const_iterartor
  27. //{
  28. // typedef _list_node<T> node;
  29. // typedef _list_const_iterartor<T> self;
  30. // node* _pnode;
  31. //}
  32. //
  33. template<class T,class Ref,class Ptr>
  34. struct _list_iterator//使用_list_iterator类来封装node*
  35. {
  36. typedef _list_node<T> node;
  37. typedef _list_iterator<T,Ref,Ptr> self;
  38. node* _pnode;
  39. //构造函数
  40. _list_iterator(node* pnode)
  41. :_pnode(pnode)
  42. {}
  43. //拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
  44. //解引用,返回左值,是拷贝,因此要用引用返回
  45. Ref operator*()
  46. {
  47. return _pnode->_val;
  48. }
  49. //结构体指针解引用,返回节点值的引用
  50. Ptr operator->()
  51. {
  52. return &_pnode->_val;
  53. }
  54. //!=重载
  55. bool operator!=(const self& s) const
  56. {
  57. return _pnode != s._pnode;
  58. }
  59. //==重载
  60. bool operator==(const self& s) const
  61. {
  62. return _pnode == s._pnode;
  63. }
  64. //前置++ it.operator(&it)
  65. self& operator++()
  66. {
  67. _pnode = _pnode->_next;
  68. return *this;
  69. }
  70. //后置++ 返回++之前的值 it.operator(&it,0)
  71. self operator++(int)
  72. {
  73. self tmp(*this);
  74. _pnode = _pnode->_next;
  75. return tmp;
  76. }
  77. //前置-- it.operator(&it)
  78. self& operator--()
  79. {
  80. _pnode = _pnode->prev;
  81. return *this;
  82. }
  83. //后置++ 返回++之前的值 it.operator(&it,0)
  84. self operator--(int)//临时对象不能用引用返回,所以self没有加&
  85. {
  86. self tmp(*this);
  87. _pnode = _pnode->_prev;
  88. return tmp;
  89. }
  90. };
  91. template<class T>
  92. class list
  93. {
  94. typedef _list_node<T> node;
  95. public:
  96. typedef _list_iterator<T,T&,T*> iterator;//重命名迭代器
  97. typedef _list_iterator<T, const T&, const T*> const_iterator;//重命名const迭代器
  98. //构造函数
  99. list()
  100. {
  101. _head = new node;//会调_list_node的构造函数
  102. _head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
  103. _head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
  104. }
  105. //拷贝构造 lt1(lt)
  106. list(const list<T>& lt)
  107. {
  108. //1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
  109. _head = new node;
  110. _head->_next = _head;
  111. _head->_prev = _head;
  112. //2.将lt的元素全部尾插到新链表
  113. for (const auto& e : lt)
  114. {
  115. push_back(e);
  116. }
  117. }
  118. 赋值运算符重载 lt1 = lt 传统写法
  119. //list<T>& operator=(list<T>& lt)
  120. //{
  121. // if(this != lt)
  122. // {
  123. // for (auto& e : lt)
  124. // {
  125. // push_back(e);
  126. // }
  127. // }
  128. //}
  129. //赋值运算符重载 lt1 = lt 现代写法
  130. list<T>& operator=(list<T>& lt)
  131. {
  132. swap(_head, lt._head);
  133. return *this;
  134. }
  135. //析构
  136. ~list()
  137. {
  138. clear();
  139. delete _head;
  140. _head = nullptr;
  141. }
  142. iterator begin()
  143. {
  144. return iterator(_head->_next);
  145. }
  146. iterator end()
  147. {
  148. return iterator(_head);//尾节点的下一个节点位置即头节点
  149. }
  150. const_iterator begin() const
  151. {
  152. return const_iterator(_head->_next);
  153. }
  154. const_iterator end() const
  155. {
  156. return const_iterator(_head);//尾节点的下一个节点位置即头节点
  157. }
  158. //插入节点
  159. void insert(iterator pos, const T& x)
  160. {
  161. assert(pos._pnode);
  162. node* newnode = new node(x);//构造节点
  163. node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
  164. //插入节点
  165. newnode->_next = pos._pnode;
  166. pos._pnode->_prev = newnode;
  167. prev->_next = newnode;
  168. newnode->_prev = prev;
  169. }
  170. //删除节点
  171. iterator erase(iterator pos)
  172. {
  173. assert(pos._pnode);//判断该位置节点是否存在
  174. assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
  175. node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
  176. node* next = pos._pnode->_next;//pos位置节点的后一个节点
  177. //删除节点
  178. delete pos._pnode;
  179. prev->_next = next;
  180. next->_prev = prev;
  181. return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
  182. }
  183. void clear()
  184. {
  185. iterator it = begin();
  186. while (it != end())
  187. {
  188. erase(it++);
  189. }
  190. }
  191. //头插
  192. void push_front(const T& x)
  193. {
  194. insert(begin(), x);
  195. }
  196. //尾插
  197. void push_back(const T& x)
  198. {
  199. insert(end()--, x);
  200. }
  201. //头删
  202. void pop_front()
  203. {
  204. erase(begin());
  205. }
  206. //尾删
  207. void pop_back()
  208. {
  209. erase(--end());
  210. }
  211. //判空
  212. bool empty()
  213. {
  214. return _head->_next == _head;
  215. }
  216. //求节点个数
  217. size_t size()
  218. {
  219. iterator it = begin();
  220. size_t sz = 0;
  221. while (it != end())//时间复杂度O(N)
  222. {
  223. it++;
  224. sz++;
  225. }
  226. return sz;
  227. }
  228. private:
  229. node* _head;
  230. };
  231. void PrintList(const list<int>& lt)
  232. {
  233. list<int>::const_iterator it = lt.begin();
  234. while (it != lt.end())
  235. {
  236. cout << it._pnode->_val << " ";
  237. it++;
  238. }
  239. cout << endl;
  240. }
  241. void test_list1()
  242. {
  243. list<int> l1;
  244. l1.push_back(5);
  245. l1.push_back(8);
  246. l1.push_back(20);
  247. l1.push_back(9);
  248. PrintList(l1);
  249. list<int> l2;
  250. l2 = l1;
  251. PrintList(l2);
  252. cout << endl;
  253. }
  254. }

016-test.cpp

  1. #include "016-list.h"
  2. #include<list>
  3. using namespace std;
  4. int main()
  5. {
  6. delia::test_list1();
  7. return 0;
  8. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/397854
推荐阅读
相关标签
  

闽ICP备14008679号