C++学习笔记_18 线性容器(vector_list_deque)总结 2021-05-18

简介: C++学习笔记_18 线性容器(vector_list_deque)总结 2021-05-18

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不会导致已有迭代器失效

各个节点位置没有任何关联,只是有个指针分别指向上一个节点和下一个节点位置

插入或者删除元素,所有其他元素位置都不会变化

所以不会导致已有迭代器失效

相关文章
|
13天前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
16天前
|
算法 C++ 容器
模拟实现c++中的list模版
模拟实现c++中的list模版
|
3月前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
55 1
|
3月前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
75 7
|
3月前
|
存储 编译器 C++
C++ initializer_list&&类型推导
在 C++ 中,`initializer_list` 提供了一种方便的方式来初始化容器和传递参数,而右值引用则是实现高效资源管理和移动语义的关键特性。尽管在实际应用中 `initializer_list&&` 并不常见,但理解其类型推导和使用方式有助于深入掌握现代 C++ 的高级特性。
31 4
|
5月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
111 2
|
4月前
|
存储 设计模式 C++
【C++】优先级队列(容器适配器)
本文介绍了C++ STL中的线性容器及其适配器,包括栈、队列和优先队列的设计与实现。详细解析了`deque`的特点和存储结构,以及如何利用`deque`实现栈、队列和优先队列。通过自定义命名空间和类模板,展示了如何模拟实现这些容器适配器,重点讲解了优先队列的内部机制,如堆的构建与维护方法。
68 0
|
5月前
|
存储 算法 C++
【C++打怪之路Lv10】-- list
【C++打怪之路Lv10】-- list
34 1
|
5月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
94 5
|
5月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
30 1