• C++ list容器的实现及讲解


    所需要的基础知识

    对C++类的基本了解 默认构造函数 操作符重载 this指针 引用 模板等知识具有一定的了解,阅读该文章会很轻松。

    链表节点

    1. template<class T>
    2. struct list_node
    3. {
    4. T _data;
    5. list_node* _next;
    6. list_node* _prev;
    7. list_node(const T& x = T())
    8. :_data(x)
    9. , _next(nullptr)//只需要将链表的节点初始化为空 在链表对象中重新进行节点的设置
    10. , _prev(nullptr)
    11. {
    12. }
    13. };

    template 是类模板 取决于链表存的数据类型 因为链表可以存许多类型的数据类型 那么只要显示传类型 就可以反复去复用 类模板 但是该写的代码编译器会帮你写 就是每个类型都会有一份代码  

    默认构造函数 对链表成员进行初始化 运用了初始化列表 在花括号中只能进行赋值 不是初始化

    链表list中的成员及函数 

    插入 

    从插入函数的实现就可以看出 该链表是双向循环链表

    1. iterator insert(iterator pos, const T& x)
    2. {
    3. node* cur = pos.node;
    4. node* newnode = new node(x);
    5. newnode->_next = cur->_next;
    6. newnode->_prev = cur;
    7. cur->_next->_prev = newnode;
    8. cur->_next = newnode;
    9. ++_size;
    10. return newnode;
    11. }

    插入和C语言中链表的插入没有区别 创建一个新节点 先对新结点进行操作 再对要插入的位置进行操作一面将插入的位置丢失 无法将双向循环链表连接上

    返回值是新节点的位置 防止你再再此处插入 方便插入

    下面的头插尾插都是对插入接口的赋用  

    1. void push_back(const T& x)
    2. {
    3. insert(--end(), x);
    4. }
    5. void push_front(const T& x)
    6. {
    7. insert(begin(), x);
    8. }

    链表的遍历靠的就是迭代器 所以传迭代器就知道了链表的节点位置 依次可以插入 如果你要任意位置插入 那就要计算 迭代器的位置 进行传入  

    删除

    1. iterator earse(iterator pos)
    2. {
    3. node* cur = pos.node;
    4. node* next = cur->_next;
    5. node* prev = cur->_prev;
    6. delete cur;
    7. next->_prev = prev;
    8. prev->_next = next;
    9. --_size;
    10. return iterator(next);
    11. }

    删除也是和之前讲过的C语言部分链表一样 删除操作要返回下一个位置的迭代器 

    返回值这样设计 就可以自动向后遍历删除

    头删尾删 就是对删除链表的复用

    1. void pop_back()
    2. {
    3. earse(--end());
    4. }
    5. void pop_front()
    6. {
    7. earse(begin());
    8. }

    链表中的迭代器调用接口

    1. iterator begin()
    2. {
    3. return _head->_next;
    4. }
    5. iterator end()
    6. {
    7. return _head;
    8. }
    9. const_iterator begin()const
    10. {
    11. return const_iterator(_head->_next);
    12. }
    13. const_iterator end()const
    14. {
    15. return const_iterator(_head);
    16. }
    17. reverse_iterator rbegin()
    18. {
    19. return reverse_iterator(end());
    20. }
    21. reverse_iterator rend()
    22. {
    23. return reverse_iterator(begin());
    24. }
    25. const_reverse_iterator rbegin()const
    26. {
    27. return const_reverse_iterator(end());
    28. }
    29. const_reverse_iterator rend()const
    30. {
    31. return const_reverse_iterator(begin());
    32. }

     迭代器再外面就是这样调用的 只需调用list函数中的正向和反向迭代器接口 就可以进行正反向遍历

    清除函数clear()

    1. void clear()
    2. {
    3. iterator it = begin();
    4. while (it != end())
    5. {
    6. it = earse(it);
    7. //it++;//因为earse()返回的是该节点下一个节点 所以不用在进行++
    8. }
    9. }

     clear()就是复用earse()函数 只保留头节点_head

    析构函数

    1. ~list()
    2. {
    3. clear();
    4. _size = 0;
    5. delete _head;
    6. _head = nullptr;
    7. }

     编译器自己写的析构函数对自定义类型不进行析构 链表节点就是自定义类型 所以需要自己写析构函数 进行数据的释放 

    赋值操作符重载“=”

    1. void swap(list& lt)
    2. {
    3. std::swap(_head, lt._head);
    4. std::swap(_size, lt._size);
    5. }
    6. list& operator=(list x)
    7. {
    8. swap(x);
    9. return *this;
    10. }

     operator=(参数) 参数是进行传值传参 传值传参就是原数据的拷贝 出了该函数这个拷贝参数x会自行销毁 会调用自己的构造函数 既然x和原数据类型值是一样的 所以就可以进行交换

    swap(x)->this->swap(x) 在list函数内部除了static修饰的函数没有this指针其他函数都有this指针 你可以在函数内部进行显示调用 但是函数参数不能出现

    如:A=B A的地址就是this B就是x; 

    链表中的类型及函数讲解

         

    • 正向迭代器

        typedef的类型简化

    1. typedef list_node Node;
    2. typedef list_iterator self;

            由于原始类型过于繁杂 所以进行类型简化 

    迭代器函数介绍:

            默认构造函数

    1. list_iterator(Node* x)
    2. :node(x)
    3. {
    4. }

            因为迭代器中存的是 链表的节点 是自定义类型 需要调用对应的默认初始化函数 

            正向迭代器的功能就是将链表正向遍历一遍 

            既然是遍历肯定要将链表中的数据打印出来那就需要对应的函数去获取

            操作符重载:

            “*”->operator*()

    1. Ref operator*()
    2. {
    3. return node->_data;
    4. }

            "->"operator->()

    1. Ptr operator->()
    2. {
    3. return &node->_data;
    4. }

         

    迭代器中存的是链表的节点 然而用迭代器是这么用的

            list::iterator it = begin()获取迭代器的首 it就是指针 所以你想获取链表节点中的数据就需要重载“*和->" *it的功能就是获取该节点的数据 只是迭代器中对其进行了重载实现 所以你可以这样使用 ”->"道理也是一样 都是迭代器进行了重载实现 功能就是通过指针进行迭代器中共有函数和成员的直接获取 淡然*it也是可以就需要叫“.”操作符进行公有成员和函数的获取

            这两个返回值的类型是不一样的 “*”既然是数据 那么就可以引用返回 只要你不销毁程序不结束没有出作用域 就可以进行传引用返回 “->"返回的就是指针类型就是T* 他的返回值前面有个&取地址符 然而”*“和”->"两者遇到一起就抵消了 就剩下node->_data也就是你想要的节点数据 我就是这样理解操作符“->"的重载实现原理的

              "前置++,--,后置++,--"

    1. self& operator++()
    2. {
    3. node = node->_next;
    4. return *this;
    5. }
    6. self& operator--()
    7. {
    8. node = node->_prev;
    9. return *this;
    10. }
    11. self operator++(int)
    12. {
    13. self tmp(*this);
    14. node = node->_next;
    15. return tmp;
    16. }
    17. self operator--(int)
    18. {
    19. self tmp(*this);
    20. node = node->_prev;
    21. return tmp;
    22. }

            前置与后置操作的区别就是前置不需要数据类型进行占位 构成操作符重载

            那么返回值类型就是迭代器的类型 返回值根据前置和后置的功能有所不同

            前置:先++/--在用 也就迭代器要返回下一个位置 由于下一个位置是存在的你不销毁或者程序不结束 那么该位置始终是有效的 所以可以返回引用 &

            后置:先用再++/-- 也就是要返回当前的值还要进行++/-- 那就要储存当前的值 不然进行完++/--找起来有点麻烦 可以找因为list的结构是双向循环链表可以向前查找 返回++/--的前一个节点就行

    拷贝当前数据出了作用域该数据会销毁 那么就不能进行引用& 返回 只能传值拷贝返回

            “比较操作符!=,==”因为不需要大于或小于 比较对象是指针

    1. bool operator==(const self& x)
    2. {
    3. return node == x.node;
    4. }
    5. bool operator!=(const self& x)
    6. {
    7. return node != x.node;
    8. }

    既然是地址的比较就应该返回 bool类型的值 (true/false) 

    反向迭代器

    由上图可以看出 反向迭代器是对正向迭代器的赋用 而且反向迭代器中成员存的是正向迭代器_it

    反向迭代器的默认构造函数

    1. reverse_iterator(iterator it)
    2. :_it(it)
    3. {
    4. }

     用正向迭代器对其进行初始化

    操作符重载——>"*和->"

    1. Ref operator*()
    2. {
    3. iterator tmp = _it;
    4. return *(--tmp);
    5. }
    6. Ptr operator->()
    7. {
    8. iterator tmp = _it;
    9. return &(operator*());
    10. }

    观察上图的反向迭代器的首部 再要获取节点的前一个位置 所以要先保存当前节点 再对当前进行前移 依次达到目的 

    “->"返回的就是指针类型就是T* 他的返回值前面有个&取地址符 然而”*“和”->"两者遇到一起就抵消了 就剩下node->_data也就是你想要的节点数据 我就是这样理解操作符“->"的重载实现原理的

    所以返回的既然是数据 那么就可以复用 之前实现的“operator*()”, 

    Ref -> T& Ptr -> T* T就是模板参数 取决于外面链表中存的是什么类型的参数

    操作符->前置“++/--”和后置"++/--"

    1. self& operator++()
    2. {
    3. --_it;
    4. return *this;
    5. }
    6. self& operator--()
    7. {
    8. ++_it;
    9. return *this;
    10. }
    11. self operator++(int)
    12. {
    13. self tmp(*this);
    14. --_it;
    15. return *this;
    16. }
    17. self operator--(int)
    18. {
    19. self tmp(*this);
    20. ++_it;
    21. return *this;
    22. }

     前置++/-- 和后置++/-- 都是对链表节点进行操作 然而迭代器中存的是链表节点 在外界对迭代器进行操作 为了使类型可以对上 所以返回self 就是反向迭代器的简化类型

    反向迭代器的运用

    前置++/-- 和后置++/--原理是一样的 先++再用 先用再++ 在正向迭代器中讲过 所以不再重复

    迭代器判断是否相等

    1. bool operator!=(const self& s)
    2. {
    3. return _it != s._it;
    4. }

     

    链表list对象的实现

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. namespace DZ
    7. {
    8. template<class T>
    9. struct list_node
    10. {
    11. T _data;
    12. list_node* _next;
    13. list_node* _prev;
    14. list_node(const T& x = T())
    15. :_data(x)
    16. , _next(nullptr)//只需要将链表的节点初始化为空 在链表对象中重新进行节点的设置
    17. , _prev(nullptr)
    18. {
    19. }
    20. };
    21. template<class T, class Ref, class Ptr>
    22. class list_iterator
    23. {
    24. public:
    25. typedef list_node Node;
    26. typedef list_iterator self;
    27. list_iterator(Node* x)
    28. :node(x)
    29. {
    30. }
    31. self& operator++()
    32. {
    33. node = node->_next;
    34. return *this;
    35. }
    36. self& operator--()
    37. {
    38. node = node->_prev;
    39. return *this;
    40. }
    41. self operator++(int)
    42. {
    43. self tmp(*this);
    44. node = node->_next;
    45. return tmp;
    46. }
    47. self operator--(int)
    48. {
    49. self tmp(*this);
    50. node = node->_prev;
    51. return tmp;
    52. }
    53. bool operator==(const self& x)
    54. {
    55. return node == x.node;
    56. }
    57. bool operator!=(const self& x)
    58. {
    59. return node != x.node;
    60. }
    61. Ref operator*()
    62. {
    63. return node->_data;
    64. }
    65. Ptr operator->()
    66. {
    67. return &node->_data;
    68. }
    69. Node* node;
    70. };
    71. template<class iterator,class Ref,class Ptr>
    72. class reverse_iterator
    73. {
    74. public:
    75. iterator _it;
    76. typedef reverse_iterator self;
    77. reverse_iterator(iterator it)
    78. :_it(it)
    79. {
    80. }
    81. Ref operator*()
    82. {
    83. iterator tmp = _it;
    84. return *(--tmp);
    85. }
    86. Ptr operator->()
    87. {
    88. iterator tmp = _it;
    89. return &(operator*());
    90. }
    91. self& operator++()
    92. {
    93. --_it;
    94. return *this;
    95. }
    96. self& operator--()
    97. {
    98. ++_it;
    99. return *this;
    100. }
    101. self operator++(int)
    102. {
    103. self tmp(*this);
    104. --_it;
    105. return *this;
    106. }
    107. self operator--(int)
    108. {
    109. self tmp(*this);
    110. ++_it;
    111. return *this;
    112. }
    113. bool operator!=(const self& s)
    114. {
    115. return _it != s._it;
    116. }
    117. };
    118. template<class T>
    119. class list
    120. {
    121. public:
    122. typedef list_node node;
    123. typedef list_iterator iterator;
    124. typedef list_iteratorconst T&, const T*> const_iterator;
    125. typedef reverse_iteratorconst T&, const T*> const_reverse_iterator;
    126. typedef reverse_iterator reverse_iterator;
    127. iterator begin()
    128. {
    129. return _head->_next;
    130. }
    131. iterator end()
    132. {
    133. return _head;
    134. }
    135. const_iterator begin()const
    136. {
    137. return const_iterator(_head->_next);
    138. }
    139. const_iterator end()const
    140. {
    141. return const_iterator(_head);
    142. }
    143. reverse_iterator rbegin()
    144. {
    145. return reverse_iterator(end());
    146. }
    147. reverse_iterator rend()
    148. {
    149. return reverse_iterator(begin());
    150. }
    151. const_reverse_iterator rbegin()const
    152. {
    153. return const_reverse_iterator(end());
    154. }
    155. const_reverse_iterator rend()const
    156. {
    157. return const_reverse_iterator(begin());
    158. }
    159. void empty_inti()
    160. {
    161. _head = new node;
    162. _head->_next = _head;
    163. _head->_prev = _head;
    164. }
    165. //it1(it2)
    166. list(const list& x)
    167. {
    168. empty_inti();
    169. for (auto& e : x)
    170. {
    171. push_back(e);
    172. }
    173. }
    174. list()
    175. {
    176. empty_inti();
    177. _size = 0;
    178. }
    179. void push_back(const T& x)
    180. {
    181. insert(--end(), x);
    182. }
    183. void push_front(const T& x)
    184. {
    185. insert(begin(), x);
    186. }
    187. void pop_back()
    188. {
    189. earse(--end());
    190. }
    191. void pop_front()
    192. {
    193. earse(begin());
    194. }
    195. //list& operator=(const list& x)
    196. //{
    197. // if (this != &x)
    198. // {
    199. // clear();
    200. // for (auto& e : x)
    201. // {
    202. // push_back(e);
    203. // }
    204. // }
    205. // return *this;
    206. //}
    207. //void swap(list& x)
    208. //{
    209. // std::swap(_size, x._size);
    210. // std::swap(_head, x._head);
    211. //}
    212. void swap(list& lt)
    213. {
    214. std::swap(_head, lt._head);
    215. std::swap(_size, lt._size);
    216. }
    217. list& operator=(list x)
    218. {
    219. swap(x);
    220. return *this;
    221. }
    222. ~list()
    223. {
    224. clear();
    225. _size = 0;
    226. delete _head;
    227. _head = nullptr;
    228. }
    229. void clear()
    230. {
    231. iterator it = begin();
    232. while (it != end())
    233. {
    234. it = earse(it);
    235. //it++;//因为earse()返回的是该节点下一个节点 所以不用在进行++
    236. }
    237. }
    238. iterator insert(iterator pos, const T& x)
    239. {
    240. node* cur = pos.node;
    241. node* newnode = new node(x);
    242. newnode->_next = cur->_next;
    243. newnode->_prev = cur;
    244. cur->_next->_prev = newnode;
    245. cur->_next = newnode;
    246. ++_size;
    247. return newnode;
    248. }
    249. iterator earse(iterator pos)
    250. {
    251. node* cur = pos.node;
    252. node* next = cur->_next;
    253. node* prev = cur->_prev;
    254. delete cur;
    255. next->_prev = prev;
    256. prev->_next = next;
    257. --_size;
    258. return iterator(next);
    259. }
    260. size_t size()
    261. {
    262. return _size;
    263. }
    264. private:
    265. size_t _size;
    266. node* _head;
    267. };
    268. void test_list1()
    269. {
    270. list<int> lt;
    271. lt.push_back(1);
    272. lt.push_back(2);
    273. lt.push_back(3);
    274. lt.push_back(4);
    275. lt.push_back(5);
    276. // 封装屏蔽底层差异和实现细节
    277. // 提供统一的访问修改遍历方式
    278. list<int>::iterator it = lt.begin();
    279. while (it != lt.end())
    280. {
    281. *it += 20;
    282. cout << *it << " ";
    283. ++it;
    284. }
    285. cout << endl;
    286. for (auto e : lt)
    287. {
    288. cout << e << " ";
    289. }
    290. cout << endl;
    291. /*set s;
    292. s.insert(1);
    293. s.insert(3);
    294. s.insert(2);
    295. s.insert(4);
    296. set::iterator sit = s.begin();
    297. while (sit != s.end())
    298. {
    299. cout << *sit << " ";
    300. ++sit;
    301. }
    302. cout << endl;*/
    303. }
    304. void test_list2()
    305. {
    306. list<int> lt;
    307. lt.push_back(1);
    308. lt.push_back(2);
    309. lt.push_back(3);
    310. lt.push_back(4);
    311. lt.push_back(5);
    312. list<int> lt1(lt);
    313. for (auto e : lt)
    314. {
    315. cout << e << " ";
    316. }
    317. cout << endl;
    318. for (auto e : lt1)
    319. {
    320. cout << e << " ";
    321. }
    322. cout << endl;
    323. list<int> lt2;
    324. lt2.push_back(10);
    325. lt2.push_back(200);
    326. lt2.push_back(30);
    327. lt2.push_back(40);
    328. lt2.push_back(50);
    329. lt1 = lt2;
    330. for (auto e : lt1)
    331. {
    332. cout << e << " ";
    333. }
    334. cout << endl;
    335. for (auto e : lt2)
    336. {
    337. cout << e << " ";
    338. }
    339. cout << endl;
    340. }
    341. struct AA
    342. {
    343. AA(int a1 = 0, int a2 = 0)
    344. :_a1(a1)
    345. , _a2(a2)
    346. {}
    347. int _a1;
    348. int _a2;
    349. };
    350. void test_list3()
    351. {
    352. list lt;
    353. lt.push_back(AA(1, 1));
    354. lt.push_back(AA(2, 2));
    355. lt.push_back(AA(3, 3));
    356. list::iterator it = lt.begin();
    357. while (it != lt.end())
    358. {
    359. //cout << (*it)._a1<<" "<<(*it)._a2 << endl;
    360. cout << it->_a1 << " " << it->_a2 << endl;
    361. cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;
    362. ++it;
    363. }
    364. cout << endl;
    365. int* p = new int;
    366. *p = 1;
    367. AA* ptr = new AA;
    368. ptr->_a1 = 1;
    369. }
    370. template<typename Container>
    371. void print_container(const Container& con)
    372. {
    373. typename Container::const_iterator it = con.begin();
    374. while (it != con.end())
    375. {
    376. cout << *it << " ";
    377. ++it;
    378. }
    379. cout << endl;
    380. }
    381. void test_list4()
    382. {
    383. list<int> lt;
    384. lt.push_back(1);
    385. lt.push_back(2);
    386. lt.push_back(3);
    387. lt.push_back(4);
    388. lt.push_back(5);
    389. //print_list(lt);
    390. print_container(lt);
    391. list lt1;
    392. lt1.push_back("1111111111111");
    393. lt1.push_back("1111111111111");
    394. lt1.push_back("1111111111111");
    395. lt1.push_back("1111111111111");
    396. lt1.push_back("1111111111111");
    397. //print_list(lt1);
    398. print_container(lt1);
    399. vector v;
    400. v.push_back("222222222222222222222");
    401. v.push_back("222222222222222222222");
    402. v.push_back("222222222222222222222");
    403. v.push_back("222222222222222222222");
    404. //print_list(v);
    405. print_container(v);
    406. }
    407. void test_list5()
    408. {
    409. list<int> lt;
    410. lt.push_back(1);
    411. lt.push_back(2);
    412. lt.push_back(3);
    413. lt.push_back(4);
    414. lt.push_back(5);
    415. list<int>::reverse_iterator it = lt.rbegin();
    416. while (it != lt.rend())
    417. {
    418. cout << *it << " ";
    419. ++it;
    420. }
    421. }
    422. }

  • 相关阅读:
    汽车行业数字化转型:时代巨变下的新机遇
    element ui中的el-tree自定义每个节点,前面加不同颜色的块
    五、java版 SpringCloud分布式微服务云架构之Java 基本数据类型
    Android热修复(代码修复+资源修复+SO动态链接库修复)
    js面试题(更新中...)
    mysqli_connect(): php_network_getaddresses: getaddrinfo failed:
    腾讯云服务器镜像TencentOS Server操作系统详细介绍
    深度学习手写简单的lstm
    使用JAVA开发情侣飞行棋小程序系统的优势与注意事项
    实用小算法
  • 原文地址:https://blog.csdn.net/2201_75324712/article/details/133207285