首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

list的模拟实现

  • 25-03-06 03:44
  • 3246
  • 12000
blog.csdn.net

一.list的简单介绍

list是一个带头双向循环的链表,通过头结点分隔开首末尾。它和vector的使用方法类似,可以进行头插尾插,++/--等操作,区别在于list的结点在内存上不是连续的。list属于双向迭代器即可以++/--,不能想vector一样可以进行随机访问。

二.list模拟实现的三个类

1.结点类

根据list的特点带头双向循环,我们可以得知,他需要一个前置结点prev,以及后置结点next,保证能向前向后找到结点。 每个结点存有数据data,我们可以用模板T来进行复用。

  1. template <class T>
  2. struct list_node
  3. {
  4. list_node(const T& x = T())//模板有x时情况
  5. :_next(nullptr)
  6. ,_prev(nullptr)
  7. ,_data(x)
  8. {}
  9. list_node* _next;
  10. list_node* _prev;
  11. T _data;
  12. };

 2.迭代器类

我们知道迭代器分为普通迭代器和const迭代器,在实现string类和vector类时我们并没有单独对迭代器这一类进行实现,这是因为vector和string类在底层空间上是连续的,可以直接进行++/--,解引用操作。但list在底层中是由指针来连接的,所以直接++/--解引用无法实现我们想要的效果。所以我们要单独提出迭代器对++/--解引用进行重载运算符操作。

  1. template<class T, class Ref, class Ptr>
  2. struct list_iterator
  3. {
  4. typedef list_node Node;
  5. typedef list_iterator Self;
  6. Node* _node;
  7. list_iterator(Node* node)
  8. :_node(node)
  9. {}
  10. T& operator*()
  11. {
  12. return _node->_data;
  13. }
  14. T& operator++()//前置
  15. {
  16. _node = _node->_next;
  17. return *this;
  18. }
  19. T& operator++(int)//后置,后置++区分前置要加入括号
  20. {
  21. Self tmp(*this);
  22. _node = _node->_next;
  23. return tmp;
  24. }
  25. T& operator--()//前置
  26. {
  27. _node = _node->_prev;
  28. return *this;
  29. }
  30. T& operator--(int)//前置
  31. {
  32. Self tmp(*this);
  33. _node = _node->_prev;
  34. return tmp;
  35. }
  36. T* operator->()
  37. {
  38. return &_node->_data;
  39. }
  40. bool operator!=(const T& it)
  41. {
  42. return _node != it._node;
  43. }
  44. bool operator==(const T& it)
  45. {
  46. return _node == _it._node;
  47. }
  48. };

如上我们对迭代器进行了初步的实现,里面的实现内容较为简单就不一一展开叙述。这时候又有了另一个问题,如果迭代器是const类型的该怎么办呢?还是像上面这样,重新封装一个const_iterator类吗?

这样操作固然是没有问题的,但是还不够简略。

我们可以仔细想想,普通迭代器与const迭代器的区别不就是多了一个const吗?所以我们可以考虑用模板来套用实现。

  1. template<class T,class Ref,class Ptr>//提供模板参数,为const类型提供模板参数,ref代替引用,ptr代替*,复用
  2. struct list_iterator
  3. {
  4. typedef list_node Node;
  5. typedef list_iterator Self;
  6. Node* _node;
  7. list_iterator(Node* node)
  8. :_node(node)
  9. {}
  10. Ref operator*()
  11. {
  12. return _node->_data;
  13. }
  14. Self& operator++()//前置
  15. {
  16. _node = _node->_next;
  17. return *this;
  18. }
  19. Self operator++(int)//后置,后置++区分前置要加入括号
  20. {
  21. Self tmp(*this);
  22. _node = _node->_next;
  23. return tmp;
  24. }
  25. Self& operator--()//前置
  26. {
  27. _node = _node->_prev;
  28. return *this;
  29. }
  30. Self operator--(int)//前置
  31. {
  32. Self tmp(*this);
  33. _node = _node->_prev;
  34. return tmp;
  35. }
  36. Ptr operator->()
  37. {
  38. return &_node->_data;
  39. }
  40. bool operator!=(const Self& it)
  41. {
  42. return _node != it._node;
  43. }
  44. bool operator==(const Self& it)
  45. {
  46. return _node == _it._node;
  47. }
  48. };

我们将原先的T&  用模板Ref来替代,T*  用模板Ptr来代替。这样就对代码进行了缩减。所以用ref和ptr模板我们就不需要关心是不是const类型迭代器,只要传就是什么,我们就无需关心。 

3.链表类

  1. template <class T>
  2. class list
  3. {
  4. typedef list_node Node;
  5. public:
  6. typedef list_iterator iterator;
  7. typedef list_iteratorconst T&, const T*> const_iterator;
  8. iterator begin()
  9. {
  10. return list_iterator(_head->_next);
  11. }
  12. iterator end()
  13. {
  14. return list_iterator(_head);
  15. }
  16. void empty_init()
  17. {
  18. _head = new Node;
  19. _head->_prev = _head;
  20. _head->_next = _head;
  21. }
  22. list()
  23. {
  24. empty_init();
  25. }
  26. list(initializer_list it)
  27. {
  28. for (auto& e : it)
  29. {
  30. push_back(e);
  31. }
  32. }
  33. list(const list& it)
  34. {
  35. empty_init();
  36. for (auto& e : it)
  37. {
  38. push_back(e);
  39. }
  40. }
  41. ~list()
  42. {
  43. clear();
  44. }
  45. void swap(list& it)
  46. {
  47. std::swap(_head, it._head);
  48. }
  49. list& operator=(list it)
  50. {
  51. swap(it);
  52. return *this;
  53. }
  54. void clear()
  55. {
  56. iterator it = begin();
  57. while (it != end())
  58. {
  59. it = erase(it);
  60. }
  61. }
  62. void push_back(const T& x)
  63. {
  64. Node* newnode = new Node(x);
  65. Node* tail = _head->_prev;
  66. newnode->_prev = tail;
  67. tail->_next = newnode;
  68. newnode->_next = _head;
  69. _head->_prev = newnode;
  70. }
  71. void push_front(const T& x)
  72. {
  73. insert(begin(), x);
  74. }
  75. void push_back(const T& x)
  76. {
  77. insert(end(), x);
  78. }
  79. void pop_front()
  80. {
  81. erase(begin());
  82. }
  83. void pop_back()
  84. {
  85. erase(--end());
  86. }
  87. void insert(iterator pos, const T& x)
  88. {
  89. Node* cur = pos._node;
  90. Node* prev = cur->_prev;
  91. Node* newnode = new Node(x);
  92. prev->_next = newnode;
  93. newnode->_prev = prev;
  94. cur->_prev = newnode;
  95. newnode->_next = cur;
  96. }
  97. iterator erase(iterator pos)
  98. {
  99. assert(pos != end());
  100. Node* cur = pos._node;
  101. Node* nextNode = cur->_next;
  102. Node* prevNode = cur->_prev;
  103. prevNode->_next = nextNode;
  104. nextNode->_prev = prevNode;
  105. delete cur;
  106. return iterator(nextNode);
  107. }
  108. private:
  109. Node* _head;
  110. };
  111. }

上述只需要注意一点,就是erase的用法。erase在官方库中是有返回值的,在使用时,例如析构时需要用变量来接收erase的返回值,否则容易导致迭代器失效。

链表部分的实现不难,基本都是先前vector类实现的功能相差无几,构造函数,拷贝函数,析构函数,迭代器的运用,以及增删等操作。

 三.总结

本篇文章最关键的内容就是对于模板的复用,在面对代码相似度较高,实现功能类似的情况下,我们需要优先考虑模板的复用。

下面放上完整代码:

  1. #pragma once
  2. #include
  3. using namespace std;
  4. namespace wu
  5. {
  6. template <class T>
  7. struct list_node
  8. {
  9. list_node(const T& x = T())//模板有x时情况
  10. :_next(nullptr)
  11. ,_prev(nullptr)
  12. ,_data(x)
  13. {}
  14. list_node* _next;
  15. list_node* _prev;
  16. T _data;
  17. };
  18. template<class T,class Ref,class Ptr>//提供模板参数,为const类型提供模板参数,ref代替引用,ptr代替*,复用
  19. struct list_iterator
  20. {
  21. typedef list_node Node;
  22. typedef list_iterator Self;
  23. Node* _node;
  24. list_iterator(Node* node)
  25. :_node(node)
  26. {}
  27. Ref operator*()
  28. {
  29. return _node->_data;
  30. }
  31. Self& operator++()//前置
  32. {
  33. _node = _node->_next;
  34. return *this;
  35. }
  36. Self operator++(int)//后置,后置++区分前置要加入括号
  37. {
  38. Self tmp(*this);
  39. _node = _node->_next;
  40. return tmp;
  41. }
  42. Self& operator--()//前置
  43. {
  44. _node = _node->_prev;
  45. return *this;
  46. }
  47. Self operator--(int)//前置
  48. {
  49. Self tmp(*this);
  50. _node = _node->_prev;
  51. return tmp;
  52. }
  53. Ptr operator->()
  54. {
  55. return &_node->_data;
  56. }
  57. bool operator!=(const Self& it)
  58. {
  59. return _node != it._node;
  60. }
  61. bool operator==(const Self& it)
  62. {
  63. return _node == _it._node;
  64. }
  65. };
  66. template <class T>
  67. class list
  68. {
  69. typedef list_node Node;
  70. public:
  71. typedef list_iterator iterator;
  72. typedef list_iteratorconst T&, const T*> const_iterator;
  73. iterator begin()
  74. {
  75. return list_iterator(_head->_next);
  76. }
  77. iterator end()
  78. {
  79. return list_iterator(_head);
  80. }
  81. void empty_init()
  82. {
  83. _head = new Node;
  84. _head->_prev = _head;
  85. _head->_next = _head;
  86. }
  87. list()
  88. {
  89. empty_init();
  90. }
  91. list(initializer_list it)
  92. {
  93. for (auto& e : it)
  94. {
  95. push_back(e);
  96. }
  97. }
  98. list(const list& it)
  99. {
  100. empty_init();
  101. for (auto& e : it)
  102. {
  103. push_back(e);
  104. }
  105. }
  106. ~list()
  107. {
  108. clear();
  109. }
  110. void swap(list& it)
  111. {
  112. std::swap(_head, it._head);
  113. }
  114. list& operator=(list it)
  115. {
  116. swap(it);
  117. return *this;
  118. }
  119. void clear()
  120. {
  121. iterator it = begin();
  122. while (it != end())
  123. {
  124. it = erase(it);
  125. }
  126. }
  127. void push_back(const T& x)
  128. {
  129. Node* newnode = new Node(x);
  130. Node* tail = _head->_prev;
  131. newnode->_prev = tail;
  132. tail->_next = newnode;
  133. newnode->_next = _head;
  134. _head->_prev = newnode;
  135. }
  136. void push_front(const T& x)
  137. {
  138. insert(begin(), x);
  139. }
  140. void push_back(const T& x)
  141. {
  142. insert(end(), x);
  143. }
  144. void pop_front()
  145. {
  146. erase(begin());
  147. }
  148. void pop_back()
  149. {
  150. erase(--end());
  151. }
  152. void insert(iterator pos, const T& x)
  153. {
  154. Node* cur = pos._node;
  155. Node* prev = cur->_prev;
  156. Node* newnode = new Node(x);
  157. prev->_next = newnode;
  158. newnode->_prev = prev;
  159. cur->_prev = newnode;
  160. newnode->_next = cur;
  161. }
  162. iterator erase(iterator pos)
  163. {
  164. assert(pos != end());
  165. Node* cur = pos._node;
  166. Node* nextNode = cur->_next;
  167. Node* prevNode = cur->_prev;
  168. prevNode->_next = nextNode;
  169. nextNode->_prev = prevNode;
  170. delete cur;
  171. return iterator(nextNode);
  172. }
  173. private:
  174. Node* _head;
  175. };
  176. }

注:本文转载自blog.csdn.net的打不了嗝的文章"https://blog.csdn.net/2401_86449430/article/details/145784183"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

133
开发工具
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top