vector
list
deque
1. //C++学习笔记_18 线性容器(vector_list_deque)总结 2. // 3. #include<iostream> 4. #include<string> 5. #include<vector> 6. #include<deque> 7. #include<list> 8. using namespace std; 9. void TestAll() 10. { 11. vector<int> iVec({ 1, 2, 3, 4}); 12. vector<int>::iterator vIt; 13. 14. cout << "元素地址: "; 15. for (vIt = iVec.begin(); vIt != iVec.end(); vIt++){ 16. //注意,这里先去 * 再去& 地址,不能 直接消掉 *vIt 是元素的引用 17. cout << &(*vIt) << " "; 18. } 19. cout << endl; 20. cout << "Capacity:" << iVec.capacity() << endl; 21. //vector 所有元素地址连续 22. 23. iVec.push_back(5); 24. cout << "首元素地址:" << &(iVec.front()) << endl; 25. cout << "Capacity:" << iVec.capacity() << endl; 26. //为什么后端插入元素,首元素地址会变? 27. //原来容量是 4 ,新插入一个元素,但是内存不够 28. // ---》重新申请一片内存,复制原数据后,再做插入操作 29. 30. iVec.push_back(11); 31. cout << "首元素地址:" << &(iVec.front()) << endl; 32. cout << "Capacity:" << iVec.capacity() << endl; 33. //再插入元素,空间足够,则直接插入 34. 35. //如果中间插入数据 ===》 插入点后的所有元素都会后移 (腾出位置) 36. 37. deque<int> iDeq{ 1, 2, 3, 4, 5 }; 38. deque<int>::iterator dIt; 39. 40. cout << endl; 41. cout << "元素地址: "; 42. for (dIt = iDeq.begin(); dIt != iDeq.end(); dIt++){ 43. cout << &(*dIt) << " "; 44. } 45. cout << endl; 46. 47. iDeq.push_back(11); 48. cout << "元素地址: "; 49. for (dIt = iDeq.begin(); dIt != iDeq.end(); dIt++){ 50. cout << &(*dIt) << " "; 51. } 52. cout << endl; 53. //新申请了一片空间,并把 11 存在这个空间的开始位置 54. 55. iDeq.push_front(22); 56. cout << "元素地址: "; 57. for (dIt = iDeq.begin(); dIt != iDeq.end(); dIt++){ 58. cout << &(*dIt) << " "; 59. } 60. cout << endl; 61. //新插入一个元素,如果第一个元素位置还有空间,直接放到第一个元素前面 62. //否则,新申请一片空间,并存在空间的最后 63. 64. //deque 在中间插入数据。把插入点位置之前的元素前移 或者 把插入点后的元素后移 65. // 前移后者后移的判断原则: 保证移动的元素最少 66. 67. list<int> iList({ 1, 2, 3, 4, 5 }); 68. list<int>::iterator lIt; 69. cout << endl << "元素地址: "; 70. for (lIt = iList.begin(); lIt != iList.end(); lIt++){ 71. cout << &(*lIt) << " "; 72. } 73. cout << endl; 74. //list 每个元素地址都不连续 75. //任意位置插入元素,都不涉及元素移动 76. } 77. 78. //vector list deque 通用的 print 函数 79. template<template <typename, typename> class C, typename T> 80. void PrintC(const C<T, allocator<T>> &tContainer) 81. { 82. typename C<T, allocator<T>>::const_iterator it; 83. for (it = tContainer.begin(); it != tContainer.end(); it++){ 84. cout << *it << " "; 85. } 86. cout << endl; 87. } 88. 89. void TestIterator() 90. { 91. //迭代器失效 92. vector<int> iVec({ 1, 2, 2, 3, 4, 5, 2, 6, 7 }); 93. vector<int>::iterator it; 94. //我们想删除 iVec中 值为 2 的元素 95. for (it = iVec.begin(); it != iVec.end(); ){ 96. if (*it == 2){ 97. //iVec.erase(it); 98. //为什么出错? erase(it) 删除了这个节点,这个节点 ++ 就没有意义了 99. //解决办法: erase(it) 返回一个 迭代器,指向下一个节点 100. it = iVec.erase(it); 101. } 102. //为什么还有一个 2 没有删掉?因为一旦2后面还是2,会被it++调过去 103. //所以删掉2的时候,不要进行 it++,直接判断当前值是否为2 104. else it++; 105. } 106. PrintC(iVec); 107. 108. //list,deque 同样存在这个问题 109. list<int> iList({ 1, 2, 3, 4, 5, 6 }); 110. list<int>::iterator lIt; 111. 112. lIt = ++iList.begin(); 113. lIt = iList.erase(lIt); 114. cout << "list::lIt: " << *lIt << endl; 115. 116. //插入元素也会导致迭代器失效 117. iVec.assign({ 1, 2, 3, 4, 5, 6, 7, 8 }); 118. it = iVec.begin() + 2; 119. cout << *it << endl; 120. 121. 122. it = iVec.insert(it, 99); //迭代器新插入的元素,占用了原来元素的地址 123. //但是 它可能涉及重新申请内存,重新申请内存,迭代器指向的内存被释放了 124. // ---> 迭代器失效 125. //iVec.insert(it, 88); 126. cout << *it << endl; 127. 128. 129. iList.insert(lIt, 99); //list 插入元素,不会改变其它元素,lIt 这个迭代器指向的内存不变 130. //插入前后,迭代器指向的值是一样的(不失效) 131. //大部分情况下,我们插入一个元素,插入后要使用这个新插入元素的迭代器 132. // --》 使用返回值: 返回新插入元素的迭代器 133. lIt = iList.insert(lIt, 88); 134. cout << "list::lIt: " << *lIt << endl; 135. 136. 137. deque<int> iDeq({ 1, 2, 3, 4, 5 }); 138. deque<int>::iterator dIt; 139. 140. dIt = iDeq.begin() + 1; 141. cout << "deque::dIt: " << *dIt << endl; 142. 143. dIt = iDeq.insert(dIt, 99); 144. //中间插入元素,99 可能占用 插入点之前的元素位置(元素前移) -- dIt 指向的元素不变 145. //也有可能 占用 插入点位置(元素后移) --》dIt 指向了 99 146. //这样插入 dIt 的变化是不确定的 147. //也涉及申请空间 --》迭代器失效 148. cout << "deque::dIt: " << *dIt << endl; 149. 150. //总之:vector list deque 的 插入删除元素的时候,都需要考虑迭代器失效问题 151. //一般情况下,都 使用 迭代器 接受返回值 152. // 插入元素 --》返回新元素的迭代器 153. // 删除元素 --》返回下一个元素的迭代器 154. } 155. 156. int main() 157. { 158. TestAll(); 159. TestIterator(); 160. system("pause"); 161. return 0; 162. }
deque 和 vector
vector 是一片连续内存,deque是多片连续内存
deque: push/pop存取数据,头部尾部都开放,vector只开放尾部
deque 对元素的访问,比vector稍慢,两次索引
deque 不存在内存重新分配机制
vector 为了保证元素的连续行,会提前为元素申请内存,不够了的话,释放掉原来的内存,重新申请一片连续内存
deque 和 list 插入元素时候,随时申请内存
使用
vector:使用的时候,一般预先估计需要多少空间,
使用 vec.reserve(N) 成员函数预先申请 N 个元素的空间
reserve(N) 函数,如果 N 比现有容量小,则 该函数不做任何事情
reserve 和 resize
reserve: 调整内存空间,不会改变vector中元素
不能调小(vector元素不能变少)
可以调大(vector 元素不会变多)
resize: 调整 vector 的元素个数, 会改变vector中元素
调小: vector 中 尾部的元素会丢失, capacity 不变
调大: vector 中 尾部新增元素,元素值由默认构造函数生成
超出 capacity,则capacity 会发生相应变化
支持设定增加元素的值 resize(n, value) 使用value调用构造函数作为新增元素
resize 和 reserve
reserve 修改的就是 capacity
list 和deque 都没有capacity
list 每新增一个节点,就申请一个节点的空间
deque 每新增一个节点,空间够了则直接存入,不够则新申请一页空间
resize
list , deque, vector 用法一样
vector、deque、list
vector 任意位置插入元素,所有元素地址都可能变化
尾部删除元素不会导致元素变化,其他位置会
deque 头尾插入元素,所有元素地址不变,中间插入元素,部分元素地址变化
删除元素对其他元素的影响和插入一样
list 任意位置插入元素,其他元素地址不变
删除元素不会导致其他元素地址变化
容器选用原则
对于少量数据来说,三者区别不大
对容器中间元素增删操作频繁:list
基本在容器首尾增删元素: deque
只需要在尾端增删元素: vector
随机访问频繁,首选 vector,其次 deque
存储的数据,顺序的进行访问和更新选 list
deque 优缺点介于vector和list之间
迭代器失效
vector ,deque 插入或者删除元素,都有可能导致迭代器失效
或者,迭代器指向一个不可预知的元素
所以,插入删除元素之后,迭代器需要重新获取
list不会导致已有迭代器失效
各个节点位置没有任何关联,只是有个指针分别指向上一个节点和下一个节点位置
插入或者删除元素,所有其他元素位置都不会变化
所以不会导致已有迭代器失效