• C++ —— 模拟实现list


    目录

    1.链表节点的构建

    2.迭代器的初步实现

    3.成员变量以及默认构造

    4.普通迭代器接口

    5.插入接口

    6.删除与find接口

    7.const迭代器实现与接口

    8.范围拷贝与拷贝构造

    9.如果实例化参数是自定义类型

    10.析构函数

    11.完整代码

    1.链表节点的构建

    链表的节点有指针与和数据域,所以无法用任何一个内置类型来表示它,我们需要自定义好节点的类型。list容器使用的是带头双向循环链表。

    1. template <class T>
    2. struct list_node //节点类型
    3. {
    4. list_node* _next;
    5. list_node* _prev;
    6. T _val;
    7. list_node(const T& val = T())
    8. :_val(val)
    9. ,_next(nullptr)
    10. ,_prev(nullptr)
    11. {}
    12. };

    2.迭代器的初步实现

    链表的节点所占用的内存空间是不连续的,所以不能使用原生指针来代替迭代器。我们需要自定义迭代器的行为(例如++是从前一个节点移动到后一个节点)。

    1. template <class T>
    2. struct list_iterator
    3. {
    4. typedef list_node node;
    5. node* pnode;
    6. list_iterator(node* p)
    7. :pnode(p)
    8. {}
    9. list_iterator& operator++()
    10. {
    11. pnode = pnode->_next;
    12. return *this;
    13. }
    14. bool operator!=(list_iterator& lt)
    15. {
    16. return pnode != lt.pnode;
    17. }
    18. };

    3.成员变量以及默认构造

    定义空容器时,容器是存在头节点(哨兵卫)的。

    1. template <class T>
    2. class list
    3. {
    4. public:
    5. typedef list_node node;
    6. typedef list_iterator iterator;
    7. void empty_init()
    8. {
    9. _head = new node(T()); //哨兵卫
    10. _head->_next = _head;
    11. _head->_prev = _head;
    12. _size = 0;
    13. }
    14. list()
    15. {
    16. empty_init(); //复用
    17. }
    18. private:
    19. node* _head;
    20. size_t size; //记录有节点个数(除哨兵卫)
    21. };

    4.普通迭代器接口

    1. iterator begin()
    2. {
    3. return iterator(_head->_next);
    4. }
    5. iterator end()
    6. {
    7. return iterator(_head); //尾后迭代器
    8. }

    5.插入接口

    插入有头插、尾插、随机插。我们重点实现随机插,头插和尾插复用随机插。

    1. void push_back(const T& val)
    2. {
    3. insert(end(), val); //在哨兵卫头插就是尾插
    4. }
    5. void push_front(const T& val)
    6. {
    7. insert(begin(), val);
    8. }
    9. iterator insert(iterator pos, const T& val)
    10. {
    11. node* newnode = new node(val);
    12. node* prev = pos.pnode->_prev;
    13. prev->_next = newnode;
    14. newnode->_prev = prev;
    15. newnode->_next = pos.pnode;
    16. pos.pnode->_prev = newnode;
    17. ++_size;
    18. return iterator(newnode); //返回插入节点的位置(防止迭代器失效)
    19. }

    6.删除与find接口

    删除有头删、尾删、随机删。我们重点实现随机删,头删和尾删复用随机删。

    1. void pop_back()
    2. {
    3. erase(end().pnode->_prev);
    4. }
    5. void pop_front()
    6. {
    7. erase(begin());
    8. }
    9. iterator erase(iterator pos)
    10. {
    11. assert(end() != pos); //不能删除哨兵卫
    12. node* prev = pos.pnode->_prev;
    13. node* next = pos.pnode->_next;
    14. prev->_next = next;
    15. next->_prev = prev;
    16. delete pos.pnode;
    17. --_size;
    18. return iterator(next); //返回删除节点的下一个节点位置(防止迭代器失效)
    19. }
    20. iterator find(iterator first, iterator last, const T& val)
    21. {
    22. assert(first != last);
    23. while (first != last)
    24. {
    25. if (*first == val) return first;
    26. ++first;
    27. }
    28. return end();
    29. }

    7.const迭代器实现与接口

    不能使用const成员函数重载,因为我们要的效果是底层const而非顶层const(即指向的内容不可变,迭代器本身可变)。所以我们有两套方案,一是再构建一个迭代器类模板;二是在原来的迭代器模板基础上添加一个模板参数。再构建一个迭代器的方案与原模板的唯一区别就是返回值不同。所以否决第一套设计方案。

    现在先统一一下迭代器接口:

    1. typedef list_iterator iterator;
    2. typedef list_iteratorconst T&> const_iterator;
    3. const_iterator begin() const
    4. {
    5. return const_iterator(_head->_next);
    6. }
    7. const_iterator end() const
    8. {
    9. return const_iterator(_head);
    10. }

    迭代器设计:

    1. template <class T,class ref> //多使用一个模板参数
    2. struct list_iterator
    3. {
    4. typedef list_node node;
    5. typedef list_iterator self; //为了方便
    6. node* pnode;
    7. list_iterator(node* p)
    8. :pnode(p)
    9. {}
    10. ref operator*()
    11. {
    12. return pnode->_val;
    13. }
    14. self& operator++()
    15. {
    16. pnode = pnode->_next;
    17. return *this;
    18. }
    19. bool operator!=(self& lt)
    20. {
    21. return pnode != lt.pnode;
    22. }
    23. };

    8.范围拷贝与拷贝构造

    我们实现更加全面的构造接口。

    1. template <class InputIterator>
    2. list(InputIterator first, InputIterator last) //范围拷贝
    3. {
    4. empty_init();
    5. while (first != last)
    6. {
    7. push_back(*first);
    8. ++first;
    9. }
    10. }
    11. void swap(list& lt)
    12. {
    13. std::swap(_head, lt._head);
    14. std::swap(_size, lt._size);
    15. }
    16. list(const list& lt) //拷贝构造现代写法
    17. {
    18. empty_init();
    19. list tmp(lt.begin(), lt.end());
    20. swap(tmp);
    21. }
    22. list& operator=(list lt) //赋值运算符重载现代写法
    23. {
    24. swap(lt);
    25. return *this;
    26. }

    9.如果实例化参数是自定义类型

    如果链表的节点是一个自定义类型,那么使用 * 将无法读取自定义类型的数据。所以我们需要完善访问自定义类型成员的功能,即 -> 运算符重载。此重载函数的返回值是实例化参数类型的指针,这个指针也有const与非const之分,并且调用此重载的对象可能是const或非const对象,也就是说迭代器可能是const迭代器与非const迭代器。那么我们依然为迭代器模板添加一个参数,并且完善迭代器的功能。

    别忘了对迭代器的重命名需要更新一下:

    1. typedef list_iterator iterator;
    2. typedef list_iteratorconst T&,const T*> const_iterator;
    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* pnode;
    7. list_iterator(node* p)
    8. :pnode(p)
    9. {}
    10. ref operator*()
    11. {
    12. return pnode->_val;
    13. }
    14. ptr operator->()
    15. {
    16. return &pnode->_val;
    17. }
    18. self& operator++()
    19. {
    20. pnode = pnode->_next;
    21. return *this;
    22. }
    23. self operator++(int) //后置++
    24. {
    25. node* tmp(pnode);
    26. pnode = pnode->_next;
    27. return tmp;
    28. }
    29. self& operator--()
    30. {
    31. pnode = pnode->_prev;
    32. return *this;
    33. }
    34. self operator--(int) //后置--
    35. {
    36. node* tmp(pnode);
    37. pnode = pnode->_prev;
    38. return tmp;
    39. }
    40. bool operator!=(const self& lt)
    41. {
    42. return pnode != lt.pnode;
    43. }
    44. bool operator==(const self& lt)
    45. {
    46. return pnode == lt.pnode;
    47. }
    48. };

    10.析构函数

    释放分为两个部分,一是不释放哨兵卫,将有效节点释放;而是全部释放。我们实现一个clear接口,让析构复用此接口。

    1. ~list()
    2. {
    3. clear();
    4. delete _head;
    5. _head = nullptr;
    6. }
    7. void clear()
    8. {
    9. auto it = begin();
    10. while (it != end())
    11. {
    12. it = erase(it);
    13. }
    14. }

    11.完整代码

    这里只实现了list容器常用的接口,并没有完全依照标准库1:1模拟实现。代码会有很多细节没有处理好,并不是会报错,而是有些地方显得不够严谨。

    1. #include
    2. #include
    3. namespace ly
    4. {
    5. template <class T>
    6. struct list_node //节点类型
    7. {
    8. list_node* _next;
    9. list_node* _prev;
    10. T _val;
    11. list_node(const T& val = T())
    12. :_val(val)
    13. ,_next(nullptr)
    14. ,_prev(nullptr)
    15. {}
    16. };
    17. template <class T,class ref,class ptr> //多使用一个模板参数
    18. struct list_iterator
    19. {
    20. typedef list_node node;
    21. typedef list_iterator self;
    22. node* pnode;
    23. list_iterator(node* p)
    24. :pnode(p)
    25. {}
    26. ref operator*()
    27. {
    28. return pnode->_val;
    29. }
    30. ptr operator->()
    31. {
    32. return &pnode->_val;
    33. }
    34. self& operator++()
    35. {
    36. pnode = pnode->_next;
    37. return *this;
    38. }
    39. self operator++(int) //后置++
    40. {
    41. node* tmp(pnode);
    42. pnode = pnode->_next;
    43. return tmp;
    44. }
    45. self& operator--()
    46. {
    47. pnode = pnode->_prev;
    48. return *this;
    49. }
    50. self operator--(int) //后置--
    51. {
    52. node* tmp(pnode);
    53. pnode = pnode->_prev;
    54. return tmp;
    55. }
    56. bool operator!=(const self& lt)
    57. {
    58. return pnode != lt.pnode;
    59. }
    60. bool operator==(const self& lt)
    61. {
    62. return pnode == lt.pnode;
    63. }
    64. };
    65. template <class T>
    66. class list
    67. {
    68. public:
    69. typedef list_node node;
    70. typedef list_iterator iterator;
    71. typedef list_iteratorconst T&,const T*> const_iterator;
    72. iterator begin()
    73. {
    74. return iterator(_head->_next);
    75. }
    76. iterator end()
    77. {
    78. return iterator(_head); //尾后迭代器
    79. }
    80. const_iterator begin() const
    81. {
    82. return const_iterator(_head->_next);
    83. }
    84. const_iterator end() const
    85. {
    86. return const_iterator(_head);
    87. }
    88. void empty_init()
    89. {
    90. _head = new node(T()); //哨兵卫
    91. _head->_next = _head;
    92. _head->_prev = _head;
    93. _size = 0;
    94. }
    95. list()
    96. {
    97. empty_init(); //复用
    98. }
    99. template <class InputIterator>
    100. list(InputIterator first, InputIterator last) //范围拷贝
    101. {
    102. empty_init();
    103. while (first != last)
    104. {
    105. push_back(*first);
    106. ++first;
    107. }
    108. }
    109. void swap(list& lt)
    110. {
    111. std::swap(_head, lt._head);
    112. std::swap(_size, lt._size);
    113. }
    114. list(const list& lt) //拷贝构造现代写法
    115. {
    116. empty_init();
    117. list tmp(lt.begin(), lt.end());
    118. swap(tmp);
    119. }
    120. list& operator=(list lt) //赋值运算符重载现代写法
    121. {
    122. swap(lt);
    123. return *this;
    124. }
    125. void pop_back()
    126. {
    127. erase(end().pnode->_prev);
    128. }
    129. void pop_front()
    130. {
    131. erase(begin());
    132. }
    133. iterator erase(iterator pos)
    134. {
    135. assert(end() != pos); //不能删除哨兵卫
    136. node* prev = pos.pnode->_prev;
    137. node* next = pos.pnode->_next;
    138. prev->_next = next;
    139. next->_prev = prev;
    140. delete pos.pnode;
    141. --_size;
    142. return iterator(next); //返回删除节点的下一个节点位置(防止迭代器失效)
    143. }
    144. iterator find(iterator first, iterator last, const T& val)
    145. {
    146. assert(first != last);
    147. while (first != last)
    148. {
    149. if (*first == val) return first;
    150. ++first;
    151. }
    152. return end();
    153. }
    154. void push_back(const T& val)
    155. {
    156. insert(end(), val); //在哨兵卫头插就是尾插
    157. }
    158. void push_front(const T& val)
    159. {
    160. insert(begin(), val);
    161. }
    162. iterator insert(iterator pos, const T& val)
    163. {
    164. node* newnode = new node(val);
    165. node* prev = pos.pnode->_prev;
    166. prev->_next = newnode;
    167. newnode->_prev = prev;
    168. newnode->_next = pos.pnode;
    169. pos.pnode->_prev = newnode;
    170. ++_size;
    171. return iterator(newnode); //返回插入节点的位置(防止迭代器失效)
    172. }
    173. private:
    174. node* _head;
    175. size_t _size; //记录有节点个数(除哨兵卫)
    176. };
    177. }

  • 相关阅读:
    总结算法题中一些常用的Java方法
    组件以及组件间的通讯
    两条命令解决移动硬盘无法弹出的问题
    Spring Boot 中使用 tkMapper
    (汇总)系统设计 - 我们如何通俗的理解那些技术的运行原理 - 汇总篇
    java基础之适配器模式[30]
    太强了吧,架构师联手总结17w字的计算机基础知识与操作系统PDF
    python经典案例(2)
    在Spring Boot中使用MyBatis访问数据库
    幂等设计详解
  • 原文地址:https://blog.csdn.net/weixin_59913110/article/details/128159117