有一个众所周知的图像(备忘单)称为“C ++容器选择”。这是为所需用途选择最佳容器的流程图。
有人知道是否已有C ++ 11版本吗?
这是前一个:
答案 0 :(得分:92)
不是我知道的,但是我猜它可以用文字方式完成。此外,图表略有偏差,因为list
通常不是一个好的容器,forward_list
也不是。这两个列表都是专门用于小众应用的容器。
要构建这样的图表,您只需要两个简单的指导原则:
首先担心性能通常是无用的。当你开始处理几千(或更多)物品时,大O的考虑才会真正起作用。
有两大类容器:
find
操作然后您可以在它们之上构建多个适配器:stack
,queue
,priority_queue
。我将把适配器留在这里,它们足够专业化,可以识别。
问题1:关联?
问题1.1:订购?
unordered_
容器,否则请使用其传统的有序订单。问题1.2:单独的密钥?
map
,否则请使用set
问题1.3:重复?
multi
,否则请使用。示例:
假设我有几个人与他们有关联的唯一ID,我想尽可能简单地从其ID中检索人员数据。
我想要一个find
函数,因此需要一个关联容器
1.1。我不关心订单,因此unordered_
容器
1.2。我的密钥(ID)与其关联的值是分开的,因此是map
1.3。该ID是唯一的,因此不会出现重复。
最终答案是:std::unordered_map<ID, PersonData>
。
问题2:内存稳定?
list
问题2.1:哪个?
list
; forward_list
只对较小的内存占用有用。问题3:动态调整大小?
{ ... }
语法),然后使用array
。它取代了传统的C阵列,但功能方便。问题4:双端?
deque
,否则请使用vector
。默认情况下,除非您需要关联容器,否则您将选择vector
。事实证明它也是Sutter and Stroustrup's recommendation。
答案 1 :(得分:48)
我喜欢Matthieu的答案,但我要重申流程图:
默认情况下,如果您需要容器,请使用std::vector
。因此,每个其他容器只能通过提供std::vector
的替代功能来证明其合理性。
std::vector
要求其内容是可移动构造的,因为它需要能够随机播放项目。放置内容并不是一个可怕的负担(注意默认构造函数不是必需的,感谢emplace
等等)。但是,大多数其他容器不需要任何特定的构造函数(再次,感谢emplace
)。因此,如果你有一个绝对无法实现移动构造函数的对象,那么你将不得不选择其他东西。
std::deque
是一般替代品,具有std::vector
的许多属性,但您只能在双端队列的两端插入。中间的插入物需要移动。 std::list
对其内容没有要求。
std::vector<bool>
是......不是。嗯,这是标准的。但它通常意义上不是vector
,因为std::vector
通常允许的操作是被禁止的。而且它肯定不包含bool
s 。
因此,如果您需要vector
个容器中的真实bool
行为,则不会从std::vector<bool>
获取该行为。因此,您必须使用std::deque<bool>
。
如果您需要在容器中查找元素,并且搜索代码不能只是一个索引,那么您可能需要放弃std::vector
以支持set
和map
。注意关键字“可能”;排序std::vector
有时是合理的选择。或者Boost.Container的flat_set/map
,它实现了一个排序的std::vector
。
现在有四种变体,每种都有自己的需求。
map
。否则请使用set
。unordered
,搜索效果绝对必须为O(1)
,而不是O(logn)
。multi
。如果您需要根据特定比较操作对项目容器进行排序,则可以使用set
。如果您需要多个项目具有相同的值,请选择multi_set
。
或者您可以使用已排序的std::vector
,但您必须对其进行排序。
当迭代器和引用无效时,有时候会引起关注。如果您需要一个项目列表,以便在其他各个地方都有这些项目的迭代器/指针,那么std::vector
的失效方法可能不合适。任何插入操作都可能导致失效,具体取决于当前的大小和容量。
std::list
提供了一个坚定的保证:迭代器及其关联的引用/指针只有在从容器中删除项目本身时才会失效。如果记忆是一个严重的问题,std::forward_list
就在那里。
如果这是一个太强大的保证,std::deque
提供了一个较弱但有用的保证。中间插入导致失效,但是头部或尾部的插入仅导致迭代器失效,而不是指向容器中项目的指针/引用。
std::vector
最后只提供便宜的插入(即便如此,如果你的容量也会变得昂贵)。
std::list
在性能方面很昂贵(每个新插入的项目都需要内存分配),但一致。它还提供偶尔必不可少的能力,可以在几乎没有性能成本的情况下对物品进行洗牌,以及在不损失性能的情况下与相同类型的其他std::list
容器交换物品。如果您需要在很多周围进行随机播放,请使用std::list
。
std::deque
提供头部和尾部的恒定时间插入/移除,但插入中间可能相当昂贵。因此,如果您需要从前面和后面添加/删除内容,std::deque
可能就是您所需要的。
应该注意的是,由于移动语义,std::vector
插入性能可能没有以前那么糟糕。一些实现实现了一种基于移动语义的项目复制(即所谓的“交换优化”),但现在移动是语言的一部分,它是标准规定的。
std::array
是一个很好的容器。它只是一个C阵列的包装器;这意味着它的大小必须在编译时中知道。如果您能接受,请使用std::array
。
话虽这么说,使用std::vector
和reserve
大小对于有界std::vector
也同样有用。这样,实际大小可能会有所不同,您只能获得一次内存分配(除非您放弃容量)。
答案 2 :(得分:23)
以上是上述流程图的C ++ 11版本。 [最初发布时没有归属于原作者Mikael Persson]
答案 3 :(得分:1)
这是一个快速旋转,虽然它可能需要工作
Should the container let you manage the order of the elements?
Yes:
Will the container contain always exactly the same number of elements?
Yes:
Does the container need a fast move operator?
Yes: std::vector
No: std::array
No:
Do you absolutely need stable iterators? (be certain!)
Yes: boost::stable_vector (as a last case fallback, std::list)
No:
Do inserts happen only at the ends?
Yes: std::deque
No: std::vector
No:
Are keys associated with Values?
Yes:
Do the keys need to be sorted?
Yes:
Are there more than one value per key?
Yes: boost::flat_map (as a last case fallback, std::map)
No: boost::flat_multimap (as a last case fallback, std::map)
No:
Are there more than one value per key?
Yes: std::unordered_multimap
No: std::unordered_map
No:
Are elements read then removed in a certain order?
Yes:
Order is:
Ordered by element: std::priority_queue
First in First out: std::queue
First in Last out: std::stack
Other: Custom based on std::vector?????
No:
Should the elements be sorted by value?
Yes: boost::flat_set
No: std::vector
您可能会注意到,这与C ++ 03版本的狂野不同,这主要是因为我真的不喜欢链接节点。除少数情况外,链接节点容器通常可以通过非链接容器来提升性能。如果您不知道这些情况是什么,并且有权访问boost,请不要使用链接节点容器。 (std :: list,std :: slist,std :: map,std :: multimap,std :: set,std :: multiset)。这个列表主要集中在小型和中型容器上,因为(A)占我们在代码中处理的99.99%,以及(B)大量元素需要自定义算法,而不是不同的容器。