• 【STL】:list的模拟实现


    朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

    C 语 言 专 栏:C语言:从入门到精通

    数据结构专栏:数据结构

    个  人  主  页 :stackY、

    C + + 专 栏   :C++

    Linux 专 栏  :Linux

    目录

    1. 基本构造

    2. 正向迭代器

    2.1 非const迭代器

    2.2 const迭代器

    2.3 正向迭代器的改进优化

    3. 修改相关接口

    3.1 insert、erase

    3.2 尾插、头插、尾删、头删、清理

    4. 拷贝构造、赋值重载、析构

    5. 完整代码


    1. 基本构造

    list的底层其实是一个带头双向循环的链表,所以在模拟实现之前可以看一下库里面怎么实现的:

    1. namespace ywh
    2. {
    3. //链表结构
    4. template<class T>
    5. struct list_node
    6. {
    7. T _data; //节点中的数据
    8. list_node* _prev; //指向前一个节点的指针
    9. list_node* _next; //指向后一个节点的指针
    10. //构造
    11. list_node(const T& x = T())
    12. :_data(x)
    13. , _prev(nullptr)
    14. , _next(nullptr)
    15. {}
    16. };
    17. //list结构
    18. template<class T>
    19. class list
    20. {
    21. public:
    22. typedef list_node Node;
    23. public:
    24. //空初始化
    25. void empty_init()
    26. {
    27. _head = new Node;
    28. _head->_prev = _head;
    29. _head->_next = _next;
    30. }
    31. //构造
    32. list()
    33. {
    34. empty_init();
    35. }
    36. private:
    37. Node* _head; //链表的头节点
    38. size_t _size; //节点个数
    39. };
    40. }

    2. 正向迭代器

    在使用list的阶段的迭代器使用方法和之前的string、vector一样,但是了解过链表的结构都知道,链表要访问下一个节点就是使用节点中的next指针指向下一个节点,需要访问里面的数据需要找到里面的data,++和解引用并不能访问链表的节点,那么迭代器的使用在底层肯定是封装加运算符重载。

    在实现迭代器的时候也需要采用封装和运算符重载,运算符重载需要用到++、--、!=、==、*、->等运算符。迭代器分为const迭代器和非const迭代器,首先先来看看非const迭代器:

    2.1 非const迭代器

    迭代器的实现一般使用struct来进行封装:

    1. namespace ljm
    2. {
    3. //链表结构
    4. template<class T>
    5. struct list_node
    6. {
    7. T _data; //节点中的数据
    8. list_node* _prev; //指向前一个节点的指针
    9. list_node* _next; //指向后一个节点的指针
    10. //构造
    11. list_node(const T& x = T())
    12. :_data(x)
    13. , _prev(nullptr)
    14. , _next(nullptr)
    15. {}
    16. };
    17. //非const正向迭代器
    18. template<class T>
    19. struct __list_iterator
    20. {
    21. typedef list_node Node;
    22. typedef __list_iterator self;
    23. Node* _node;
    24. //迭代器构造
    25. __list_iterator(Node* node)
    26. :_node(node)
    27. {}
    28. //前置
    29. //operator++
    30. self& operator++()
    31. {
    32. return _node->_next;
    33. }
    34. //operator--
    35. self& operator--()
    36. {
    37. return _node->_prev;
    38. }
    39. //后置
    40. self operator++(int)
    41. {
    42. self* tmp(_node);
    43. _node = _node->_next;
    44. return tmp;
    45. }
    46. //operator--
    47. self operator--(int)
    48. {
    49. self* tmp(_node);
    50. _node = _node->_prev;
    51. return tmp;
    52. }
    53. //operator*
    54. T& operator*()
    55. {
    56. return _node->_data;
    57. }
    58. //operator->
    59. T* operator->()
    60. {
    61. return &_node->_data;
    62. }
    63. //operator!=
    64. bool operator!=(const self& s)
    65. {
    66. return _node != s._node;
    67. }
    68. //operator==
    69. bool operator==(const self& s)
    70. {
    71. return _node == s._node;
    72. }
    73. };
    74. //list结构
    75. template<class T>
    76. class list
    77. {
    78. public:
    79. typedef list_node Node;
    80. public:
    81. //空初始化
    82. void empty_init()
    83. {
    84. _head = new Node;
    85. _head->_prev = _head;
    86. _head->_next = _head;
    87. }
    88. //构造
    89. list()
    90. {
    91. empty_init();
    92. }
    93. ///正向迭代器
    94. iterator begin()
    95. {
    96. return iterator(_head->_next); //使用匿名对象进行构造
    97. }
    98. iterator end()
    99. {
    100. return iterator(_head);
    101. }
    102. private:
    103. Node* _head; //链表的头节点
    104. size_t _size; //节点个数
    105. };
    106. }

    2.2 const迭代器

    迭代器中涉及到修改的就是*和->,const迭代器的作用是指向的内容不能修改,本身不能修改,所以需要重新进行封装:

    1. namespace ljm
    2. {
    3. //链表结构
    4. template<class T>
    5. struct list_node
    6. {
    7. T _data; //节点中的数据
    8. list_node* _prev; //指向前一个节点的指针
    9. list_node* _next; //指向后一个节点的指针
    10. //构造
    11. list_node(const T& x = T())
    12. :_data(x)
    13. , _prev(nullptr)
    14. , _next(nullptr)
    15. {}
    16. };
    17. //非const正向迭代器
    18. //...
    19. //const正向迭代器
    20. template<class T>
    21. struct __list_const_iterator
    22. {
    23. typedef list_node Node;
    24. typedef __list_const_iterator self;
    25. Node* _node;
    26. //迭代器构造
    27. __list_const_iterator(Node* node)
    28. :_node(node)
    29. {}
    30. //前置
    31. //operator++
    32. self& operator++()
    33. {
    34. _node = _node->_next;
    35. return *this;
    36. }
    37. //operator--
    38. self& operator--()
    39. {
    40. _node = _node->_prev;
    41. return *this;
    42. }
    43. //后置
    44. self operator++(int)
    45. {
    46. self* tmp(_node);
    47. _node = _node->_next;
    48. return tmp;
    49. }
    50. //operator--
    51. self operator--(int)
    52. {
    53. self* tmp(_node);
    54. _node = _node->_prev;
    55. return tmp;
    56. }
    57. //operator*
    58. const T& operator*()
    59. {
    60. return _node->_data;
    61. }
    62. //operator->
    63. const T* operator->()
    64. {
    65. return &_node->_data;
    66. }
    67. //operator!=
    68. bool operator!=(const self& s)
    69. {
    70. return _node != s._node;
    71. }
    72. //operator==
    73. bool operator==(const self& s)
    74. {
    75. return _node == s._node;
    76. }
    77. };
    78. //list结构
    79. template<class T>
    80. class list
    81. {
    82. public:
    83. typedef list_node Node;
    84. typedef __list_iterator iterator;
    85. typedef __list_const_iterator const_iterator;
    86. public:
    87. 基本构造///
    88. //空初始化
    89. void empty_init()
    90. {
    91. _head = new Node;
    92. _head->_prev = _head;
    93. _head->_next = _head;
    94. }
    95. //构造
    96. list()
    97. {
    98. empty_init();
    99. }
    100. ///正向迭代器
    101. iterator begin()
    102. {
    103. return iterator(_head->_next); //使用匿名对象进行构造
    104. }
    105. iterator end()
    106. {
    107. return iterator(_head);
    108. }
    109. const_iterator begin() const
    110. {
    111. return const_iterator(_head->_next);
    112. }
    113. const_iterator end() const
    114. {
    115. return const_iterator(_head);
    116. }
    117. private:
    118. Node* _head; //链表的头节点
    119. size_t _size; //节点个数
    120. };
    121. }

    2.3 正向迭代器的改进优化

    上面的这两种写法不难看出有很多代码都是重复出现的,使得代码非常冗余,那么怎么将这些冗余的代码进行合并呢?

    我们可以看一下库里面如何实现:

    采用泛型编程,使用模板,将这一转化的工作交给编译器,而我们只需传递const参数和非const参数即可:

    1. namespace ywh
    2. {
    3. //链表结构
    4. template<class T>
    5. struct list_node
    6. {
    7. T _data; //节点中的数据
    8. list_node* _prev; //指向前一个节点的指针
    9. list_node* _next; //指向后一个节点的指针
    10. //构造
    11. list_node(const T& x = T())
    12. :_data(x)
    13. , _prev(nullptr)
    14. , _next(nullptr)
    15. {}
    16. };
    17. //非const正向迭代器
    18. // 类型模板参数 传递引用 传递指针
    19. template<class T, class Ref, class Ptr>
    20. struct __list_iterator
    21. {
    22. typedef list_node Node;
    23. typedef __list_iterator self;
    24. Node* _node;
    25. //迭代器构造
    26. __list_iterator(Node* node)
    27. :_node(node)
    28. {}
    29. //前置
    30. //operator++
    31. self& operator++()
    32. {
    33. _node = _node->_next;
    34. return *this;
    35. }
    36. //operator--
    37. self& operator--()
    38. {
    39. _node = _node->_prev;
    40. return *this;
    41. }
    42. //后置
    43. self operator++(int)
    44. {
    45. self* tmp(_node);
    46. _node = _node->_next;
    47. return tmp;
    48. }
    49. //operator--
    50. self operator--(int)
    51. {
    52. self* tmp(_node);
    53. _node = _node->_prev;
    54. return tmp;
    55. }
    56. //operator*
    57. Ref operator*()
    58. {
    59. return _node->_data;
    60. }
    61. //operator->
    62. Ptr operator->()
    63. {
    64. return &_node->_data;
    65. }
    66. //operator!=
    67. bool operator!=(const self& s)
    68. {
    69. return _node != s._node;
    70. }
    71. //operator==
    72. bool operator==(const self& s)
    73. {
    74. return _node == s._node;
    75. }
    76. };
    77. //list结构
    78. template<class T>
    79. class list
    80. {
    81. public:
    82. typedef list_node Node;
    83. typedef __list_iterator iterator; //非const迭代器
    84. typedef __list_iteratorconst T&, const T*> const_iterator; //const迭代器
    85. public:
    86. 基本构造///
    87. //空初始化
    88. void empty_init()
    89. {
    90. _head = new Node;
    91. _head->_prev = _head;
    92. _head->_next = _head;
    93. }
    94. //构造
    95. list()
    96. {
    97. empty_init();
    98. }
    99. ///正向迭代器
    100. iterator begin()
    101. {
    102. return iterator(_head->_next); //使用匿名对象进行构造
    103. }
    104. iterator end()
    105. {
    106. return iterator(_head);
    107. }
    108. const_iterator begin() const
    109. {
    110. return const_iterator(_head->_next);
    111. }
    112. const_iterator end() const
    113. {
    114. return const_iterator(_head);
    115. }
    116. private:
    117. Node* _head; //链表的头节点
    118. size_t _size; //节点个数
    119. };
    120. }

    3. 修改相关接口

    3.1 insert、erase

    在pos位置进行插入删除比较简单,需要进行链接新节点,改变节点中指针的指向即可,库里面对于插入和删除都带有返回值,那么我们在实现的时候也加上返回值:

    1. ///修改相关接口
    2. //在pos位置插入节点
    3. iterator insert(iterator pos, const T& x)
    4. {
    5. //创建新新节点
    6. Node* newnode = new Node(x);
    7. //链接节点
    8. Node* cur = pos._node;
    9. Node* prev = cur->_prev;
    10. prev->_next = newnode;
    11. newnode->_prev = prev;
    12. newnode->_next = cur;
    13. cur->_prev = newnode;
    14. //更新节点个数
    15. ++_size;
    16. //返回新节点的位置
    17. return iterator(newnode);
    18. }
    19. //删掉pos位置的节点
    20. iterator erase(iterator pos)
    21. {
    22. //保存相对位置
    23. Node* cur = pos._node;
    24. Node* prev = cur->_prev;
    25. Node* next = cur->_next;
    26. //链接节点
    27. delete cur;
    28. prev->_next = next;
    29. next->_prev = prev;
    30. //更新节点个数
    31. --_size;
    32. //返回pos的下一个位置
    33. return iterator(next);
    34. }

    在进行insert之后可以看到迭代器是不会失效的,但是在erase之后pos位置会被释放,因此erase之后迭代器会失效,在使用前先更新迭代器。

    3.2 尾插、头插、尾删、头删、清理

    当实现了insert和erase之后,实现这些接口直接复用即可:

    1. //尾插
    2. void push_back(const T& x)
    3. {
    4. insert(end(), x);
    5. }
    6. //头插
    7. void push_front(const T& x)
    8. {
    9. insert(begin(), x);
    10. }
    11. //尾删
    12. void pop_back()
    13. {
    14. erase(--end());
    15. }
    16. //头删
    17. void pop_front()
    18. {
    19. erase(begin());
    20. }
    21. //清理
    22. void clear()
    23. {
    24. iterator it = begin();
    25. while (it != end())
    26. {
    27. it = erase(it);
    28. }
    29. }

    4. 拷贝构造、赋值重载、析构

    在这里我们都是采用现代写法:

     拷贝构造直接使用迭代器依次遍历进行尾插。

    1. //拷贝构造
    2. list(const list& lt)
    3. {
    4. //先创建空节点
    5. empty_init();
    6. //依次尾插即可
    7. for (auto e : lt)
    8. {
    9. push_back(e);
    10. }
    11. }
    12. //operator=
    13. void swap(list& lt)
    14. {
    15. std::swap(_head, lt._head);
    16. std::swap(_size, lt._size);
    17. }
    18. list& operator=(list lt)
    19. {
    20. swap(lt);
    21. return *this;
    22. }
    23. //析构
    24. ~list()
    25. {
    26. clear();
    27. delete _head;
    28. _head = nullptr;
    29. _size = 0;
    30. }

    5. 完整代码

     头文件:List.h

    1. #pragma once
    2. namespace ywh
    3. {
    4. //链表结构
    5. template<class T>
    6. struct list_node
    7. {
    8. T _data; //节点中的数据
    9. list_node* _prev; //指向前一个节点的指针
    10. list_node* _next; //指向后一个节点的指针
    11. //构造
    12. list_node(const T& x = T())
    13. :_data(x)
    14. , _prev(nullptr)
    15. , _next(nullptr)
    16. {}
    17. };
    18. //非const正向迭代器
    19. // 类型模板参数 传递引用 传递指针
    20. template<class T, class Ref, class Ptr>
    21. struct __list_iterator
    22. {
    23. typedef list_node Node;
    24. typedef __list_iterator self;
    25. Node* _node;
    26. //迭代器构造
    27. __list_iterator(Node* node)
    28. :_node(node)
    29. {}
    30. //前置
    31. //operator++
    32. self& operator++()
    33. {
    34. _node = _node->_next;
    35. return *this;
    36. }
    37. //operator--
    38. self& operator--()
    39. {
    40. _node = _node->_prev;
    41. return *this;
    42. }
    43. //后置
    44. self operator++(int)
    45. {
    46. self* tmp(_node);
    47. _node = _node->_next;
    48. return tmp;
    49. }
    50. //operator--
    51. self operator--(int)
    52. {
    53. self* tmp(_node);
    54. _node = _node->_prev;
    55. return tmp;
    56. }
    57. //operator*
    58. Ref operator*()
    59. {
    60. return _node->_data;
    61. }
    62. //operator->
    63. Ptr operator->()
    64. {
    65. return &_node->_data;
    66. }
    67. //operator!=
    68. bool operator!=(const self& s)
    69. {
    70. return _node != s._node;
    71. }
    72. //operator==
    73. bool operator==(const self& s)
    74. {
    75. return _node == s._node;
    76. }
    77. };
    78. //list结构
    79. template<class T>
    80. class list
    81. {
    82. public:
    83. typedef list_node Node;
    84. typedef __list_iterator iterator; //非const迭代器
    85. typedef __list_iteratorconst T&, const T*> const_iterator; //const迭代器
    86. public:
    87. 基本构造///
    88. //空初始化
    89. void empty_init()
    90. {
    91. _head = new Node;
    92. _head->_prev = _head;
    93. _head->_next = _head;
    94. }
    95. //构造
    96. list()
    97. {
    98. empty_init();
    99. }
    100. //拷贝构造
    101. list(const list& lt)
    102. {
    103. //先创建空节点
    104. empty_init();
    105. //依次尾插即可
    106. for (auto e : lt)
    107. {
    108. push_back(e);
    109. }
    110. }
    111. //operator=
    112. void swap(list& lt)
    113. {
    114. std::swap(_head, lt._head);
    115. std::swap(_size, lt._size);
    116. }
    117. list& operator=(list lt)
    118. {
    119. swap(lt);
    120. return *this;
    121. }
    122. //析构
    123. ~list()
    124. {
    125. clear();
    126. delete _head;
    127. _head = nullptr;
    128. _size = 0;
    129. }
    130. ///正向迭代器
    131. iterator begin()
    132. {
    133. return iterator(_head->_next); //使用匿名对象进行构造
    134. }
    135. iterator end()
    136. {
    137. return iterator(_head);
    138. }
    139. const_iterator begin() const
    140. {
    141. return const_iterator(_head->_next);
    142. }
    143. const_iterator end() const
    144. {
    145. return const_iterator(_head);
    146. }
    147. ///修改相关接口
    148. //在pos位置插入节点
    149. iterator insert(iterator pos, const T& x)
    150. {
    151. //创建新新节点
    152. Node* newnode = new Node(x);
    153. //链接节点
    154. Node* cur = pos._node;
    155. Node* prev = cur->_prev;
    156. prev->_next = newnode;
    157. newnode->_prev = prev;
    158. newnode->_next = cur;
    159. cur->_prev = newnode;
    160. //更新节点个数
    161. ++_size;
    162. //返回新节点的位置
    163. return iterator(newnode);
    164. }
    165. //删掉pos位置的节点
    166. iterator erase(iterator pos)
    167. {
    168. //保存相对位置
    169. Node* cur = pos._node;
    170. Node* prev = cur->_prev;
    171. Node* next = cur->_next;
    172. //链接节点
    173. delete cur;
    174. prev->_next = next;
    175. next->_prev = prev;
    176. //更新节点个数
    177. --_size;
    178. //返回pos的下一个位置
    179. return iterator(next);
    180. }
    181. //尾插
    182. void push_back(const T& x)
    183. {
    184. insert(end(), x);
    185. }
    186. //头插
    187. void push_front(const T& x)
    188. {
    189. insert(begin(), x);
    190. }
    191. //尾删
    192. void pop_back()
    193. {
    194. erase(--end());
    195. }
    196. //头删
    197. void pop_front()
    198. {
    199. erase(begin());
    200. }
    201. //清理
    202. void clear()
    203. {
    204. iterator it = begin();
    205. while (it != end())
    206. {
    207. it = erase(it);
    208. }
    209. }
    210. //节点个数
    211. size_t size()
    212. {
    213. return _size;
    214. }
    215. private:
    216. Node* _head; //链表的头节点
    217. size_t _size; //节点个数
    218. };
    219. }

    朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

  • 相关阅读:
    史上最全面试题版!看完吊打面试官!七夕来袭!是时候展现专属于程序员的浪漫了 10万字+
    Kernel: 宏:__KERNEL__
    从传统到智能 | 拓世法宝AI智能直播一体机为商家注入活力
    二叉树的顺序结构及实现
    Vue - 快速入门,这一套就够了!(Vue core + 案例 + 效果演示)
    C++-Cmake指令:find_library【该命令用于查找库(动态库或者静态库),当构建依赖于第三方库/系统库,可以使用该命令来查找并使用库】
    合伙保密协议
    小白学习spring第四天
    Centos7的系统管理(systemctl、系统运行级别、关机)
    Tomcat安装步骤及详细配置教程(2022最新版)
  • 原文地址:https://blog.csdn.net/Yikefore/article/details/134090437