Bla*_*Bat 131 c++ c++-faq c++11
有一个众所周知的图像(备忘单)称为"C++容器选择".这是为所需用途选择最佳容器的流程图.
有人知道是否已有C++ 11版本吗?
这是前一个:
Mat*_* M. 93
不是我所知道的,但我猜它可以用文字方式完成.此外,图表略有偏差,因为list
一般来说这不是一个好的容器,也不是forward_list
.这两个列表都是专门用于小众应用的容器.
要构建这样的图表,您只需要两个简单的指导原则:
担心性能一开始通常是无用的.当您开始处理数千(或更多)项目时,大O考虑因素才会真正起作用.
容器有两大类:
find
操作然后你就可以建立在它们上面几个适配器:stack
,queue
,priority_queue
.我将把适配器留在这里,它们足够专业化,可以识别.
问题1:联想?
问题1.1:订购?
unordered_
容器,否则请使用其传统的有序订单.问题1.2:单独的密钥?
map
,否则使用aset
问题1.3:重复?
multi
,否则请使用.例:
假设我有几个具有与之关联的唯一ID的人,并且我想尽可能简单地从其ID中检索人员数据.
我想要一个find
函数,因此是一个关联容器
1.1.我不在乎订单,因此是一个unordered_
容器
1.2.我的密钥(ID)与它关联的值是分开的,因此amap
1.3.ID是唯一的,因此不会出现重复.
最后的答案是:std::unordered_map<ID, PersonData>
.
问题2:记忆稳定吗?
list
问题2.1:哪个?
list
; a forward_list
仅对较小的内存占用量有用.问题3:动态大小?
{ ... }
语法),则使用array
.它取代了传统的C阵列,但功能方便.问题4:双端?
deque
,否则使用a vector
.您将注意到,默认情况下,除非您需要关联容器,否则您的选择将是a vector
.事实证明这也是Sutter和Stroustrup的推荐.
Nic*_*las 50
我喜欢Matthieu的答案,但我要重申流程图:
默认情况下,如果您需要一个容器的容器,请使用std::vector
.因此,每个其他容器只能通过提供一些替代功能来证明std::vector
.
std::vector
要求其内容是可移动构造的,因为它需要能够对周围的项目进行洗牌.放置内容并不是一个可怕的负担(请注意,默认构造函数不是必需的,多亏了emplace
等等).但是,大多数其他容器不需要任何特定的构造函数(再次,谢谢emplace
).因此,如果您有一个绝对无法实现移动构造函数的对象,那么您将不得不选择其他东西.
A std::deque
将是一般替换,具有许多属性std::vector
,但您只能在双端队列的两端插入.中间的插入物需要移动.A std::list
不要求其内容.
std::vector<bool>
不是.嗯,这是标准的.但它不是vector
通常意义上的,因为std::vector
通常允许的操作是被禁止的.它肯定不包含bool
s.
因此,如果你需要vector
来自bool
s 容器的真实行为,你就不会从中获取它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
,如果您需要多个项目具有相同的搜索标签.如果您需要根据特定的比较操作对项目容器进行排序,则可以使用a 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
.这样,实际大小可能会有所不同,您只能获得一次内存分配(除非您将容量降低).
Was*_*aze 24
这是上面流程图的C++ 11版本.[最初发布时没有归属于原作者Mikael Persson ]