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

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

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

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

相关文章
|
3天前
|
存储 C++ 索引
C++标准库容器的使用
C++标准库容器的使用
13 1
|
3天前
|
存储 C++ 容器
C++标准库容器的基本用法
C++标准库容器的基本用法
11 0
|
5天前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(下)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
14 1
|
5天前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
8 1
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
|
5天前
|
存储 安全 C语言
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(上)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
10 2
|
6天前
|
存储 算法 C语言
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)(下)
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)
18 2
|
6天前
|
算法 C语言 C++
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)(中)
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)
15 0
|
6天前
|
缓存 算法 C语言
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)(上)
从C语言到C++_19(容器适配器+stack和queue模拟实现+优先级队列priority_queue)
16 0
|
6天前
|
C语言 容器
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器(下 )
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器
5 1
|
6天前
|
C语言 计算机视觉
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器(中)
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器
11 1