本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。
STL容器分类
- 顺序(序列)容器:vector, list, deque, string,stack( 适配器类), queue( 适配器类), priority queues( 适配器类)
- 关联容器:set, multiset, map, multimap, bitset,hash_set, hash_map, hash_multiset, hash_multimap
STL容器对比
vector | deque | list | set | multiset | map | multimap | |
---|---|---|---|---|---|---|---|
名称 | 向量容器 | 双向队列容器 | 列表容器 | 集合 | 多重集合 | 映射 | 多重映射 |
内部数据结构 | 连续存储的数组形式(一端开口的组) | 连续或分段连续存储数组(两端开口的数组) | 双向环状链表 | 红黑树(平衡检索二叉树) | 红黑树 | 红黑树 | 红黑树 |
特 点 | 获取元素效率很高,插入和删除的效率很低 | 获取元素效率较高,插入和删除的效率较高 | 获取元素效率很低,插入和删除的效率很高 | 1.键(关键字)和值(数据)相等(就是模版只有一个参数,键和值合起来)2.键唯一3.元素默认按升序排列 | 1.键和值相等2.键可以不唯一3.元素默认按升序排列 | 1.键和值分开(模版有两个参数,前面是键后面是值)2.键唯一3.元素默认按键的升序排列 | 1.键和值分开2.键可以不唯一3.元素默认按键的升序排列 |
头文件 | #include | #include | #include | #include | #include | #include | #include |
操作元素的方式 | 下标运算符:[0](可以用迭代器,但插入删除操作时会失效) | 下标运算符或迭代器 | 只能用迭代器(不断用变量值来递推新值,相当于指针),不支持使用下标运算符 | 迭代器 | 迭代器 | 迭代器 | 迭代器 |
插入删除操作迭代器是否失效 | 插入和删除元素都会使迭代器失效 | 插入任何元素都会使迭代器失效。删除头和尾元素,指向被删除节点迭代器失效,而删除中间元素会使所有迭代器失效 | 插入,迭代器不会失效。删除,指向被删除节点迭代器失效 | 插入,迭代器不会失效。删除,指向被删除节点迭代器失效 | 插入,迭代器不会失效。删除,指向被删除节点迭代器失效 | 插入,迭代器不会失效。删除,指向被删除节点迭代器失效 | 插入,迭代器不会失效。删除,指向被删除节点迭代器失效 |
容器成员对比
迭代器失效的问题
顺序容器,在内存中是一块连续的内存,当删除一个元素后,内存中的数据会发生移动,以保证数据的紧凑。所以删除一个数据后,其他数据的地址发生了变化,之前获取的迭代器根据原有的信息就访问不到正确的数据。
1、对于节点式容器(map, list, set)元素的删除,插入操作会导致指向该元素的迭代器失效,其他元素迭代器不受影响。
2、对于顺序式容器(vector)元素的删除、插入操作会导致指向该元素以及后面的元素的迭代器失效。
vector迭代器的几种失效的情况:
1、当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2、当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。
3、当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。
deque迭代器的失效情况: 在C++Primer一书中是这样限定的:
1、在deque容器首部或者尾部插入元素不会使得任何迭代器失效。
2、在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效。
3、在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。
其他
-
当对象很大时,建立指针的容器而不是对象的容器
STL基于拷贝的方式的来工作,任何需要放入STL中的元素,都会被复制。这也好理解,STL工作的容器是在堆内开辟的一块新空间,而我们自己的变量一般存放在函数栈或另一块堆空间中。为了能够完全控制STL自己的元素,为了能在自己的地盘随心干活,这就涉及到复制。而如果复制的对象很大,由复制带来的性能代价也不小,对于大对象的操作,使用指针来代替对象能消除这方面的代价
-
只涉及到指针拷贝操作, 没有额外类的构造函数和赋值构造函数的调用
不可取:
vecttor vt1;
vt1.push_bach(myBigObj);
可取:
vecttor vt2;
vt2.push_bach(new BigObj());
-
容器销毁前需要自行销毁指针所指向的对象 , 否则就造成了内存泄漏;
-
使用排序等算法时,需要构造基于对象的比较函数,如果使用默认的比较函数,其结果是基于指针大小的比较,而不是对象的比较;
-
在使用vector的时候,需要有一点注意:尽量少使用erase,因为在发生erase的时候,会发生一次拷贝,vector要保持结构的完整性,会把从操作对象后的每一个成员都进行一次拷贝,并前移一位,但是在最后一个成员发生移动的时候,如果成员是一个非常规类型,会发生析构,那该成员以及该成员的拷贝都将被删除
-
C++ STL 的 vector 容器在 clear() 之后不会释放内存,需要 swap(empty vector),这是有意为之(C++11 里增加了 shrink_to_fit() 函数)。不要记成了所有 STL 容器都需要swap(empty one) 来释放内存。
事实上其他容器(map/set/list/deque)都只需要 clear() 就能释放内存。
只有含 reserve()/capacity() 成员函数的容器才需要用 swap 来释放空间,而 C++ 里只有 vector 和 string 这两个符合条件。