注:这是篇搬运工文章,是在读了《Effective STL》这本书后对一些知识点的总结、摘录,希望和大家一起学习共享。
题外话:在网上看到很多人喷C++,喷STL,其实我曾经也会喷C#、喷Java,...但是随着工作经验的增长,阅读书籍的增多,知识面的扩展,我对种类繁多的语言以及技术产生的是敬畏,再也不敢也不会信口开河的去喷,每种语言都有自己的优缺点,也正是这些优缺点,决定了其适用场景。送大家一句今天刚看到的话“工程师会思考项目用什么语言好,码农会思考哪种编程语言不好”。
经常听到有人抱怨STL性能不好,不排除你的公司十分牛X而且对性能要求极高,至少我想绝大多数企业和工程师自己写不出比STL更好的库,如果你不相信,请先看看《STL源码剖析》这本书。我想大多数情况是你没有选择对相应的容器和没有正确的使用。
容器分类
-
标准STL序列容器:vector,string,deque,list;
-
标准STL关联容器:set,multiset,map,multimap;
-
非标准关联容器(基于散列表):hash_set,hash_multiset,hash_map,hash_multimap;
-
连续内存容器:vector,string,deque;
-
基于节点的容器:list,标准STL关联容器;
主要容器分析
-
连续内存容器(也叫做基于数组的容器)在一个或多个(动态分配)的内存块中保存它们的元素。如果一个新元素被插入或者已存元素被删除,其他在同一个内存块的元素就必须向上或者向下移动来为新元素提供空间或者填充原来被删除的元素所占的空间。这种移动影响了效率和异常安全;
-
基于节点的容器在每个内存块(动态分配)中只保存一个元素。容器元素的插入或删除只影响指向节点的指针,而不是节点自己的内容。所以当有东西插入或删除时,元素值不需要移动。
容器选择
-
vector、list和deque提供给程序员不同的复杂度,因此应该这么用:vector是一种可以默认使用的序列类型,当很频繁地对序列中部进行插入和删除时应该用list,当大部分插入和删除发生在序列的头或尾时可以选择deque这种数据结构。
-
你需要“可以在容器的任意位置插入一个新元素”的能力吗?如果是,你需要序列容器,关联容器做不到。
-
你关心元素在容器中的顺序吗?如果不,散列容器就是可行的选择。否则,你要避免使用散列容器。
-
你需要哪一类迭代器?如果必须是随机访问迭代器,在技术上你就只能限于vector、deque和string。
-
当插入或者删除数据时,是否非常在意容器内现有元素的移动?如果是,你就必须放弃连续内存容器。
-
容器中的数据的内存布局需要兼容C吗?如果是,你就只能用vector。
-
查找速度很重要吗?如果是,你就应该看看散列容器(优于)排序的vector(优于)标准的关联容器大概是这个顺序。
-
你需要插入和删除的事务性语义吗?也就是说,你需要有可靠地回退插入和删除的能力吗?如果是,你就需要使用基于节点的容器。如果你需要多元素插入的事务性语义,你就应该选择list,因为list是唯一提供多元素插入事务性语义的标准容器。事务性语义对于有兴趣写异常安全代码的程序员来说非常重要。
-
你要把迭代器、指针和引用的失效次数减到最少吗?如果是,你就应该使用基于节点的容器,因为在这些容器上进行插入和删除不会使迭代器、指针和引用失效(除非它们指向你删除的元素)。一般来说,在连续内存容器上插入和删除会使所有指向容器的迭代器、指针和引用失效。
-
你需要具有有以下特性的序列容器吗:1)可以使用随机访问迭代器;2)只要没有删除而且插入只发生在容器结尾,指针和引用的数据就不会失效?这个一个非常特殊的情况,但如果你遇到这种情况,deque就是你梦想的容器。(有趣的是,当插入只在容器结尾时,deque的迭代器也可能会失效,deque是唯一一个“在迭代器失效时不会使它的指针和引用失效”的标准STL容器。)