对于STL或!STL,这是[关闭]的问题

问题描述 投票:42回答:15

毫无疑问,我会选择将STL用于大多数C ++编程项目。最近我提出了这个问题,“有没有你不会使用STL的情况?”......

我越是想到它,我就越意识到也许应该是我选择不使用STL的情况......例如,一个非常大的长期项目,其代码库预计将持续数年...也许是真正符合项目需求的定制容器解决方案值得最初的开销吗?你怎么想,有没有你选择不STL的情况?

c++ stl containers
15个回答
31
投票

具有严格内存要求的项目(例如嵌入式系统)可能不适合STL,因为很难控制和管理从堆中获取和返回的内容。正如Evan所说,编写适当的分配器可以帮助解决这个问题,但如果你计算所使用的每个字节或者关注内存碎片,那么手动推出针对您的特定问题量身定制的解决方案可能更为明智,因为STL已经过优化用于最常用的用途。

您也可以选择不对特定情况使用STL,因为存在不在当前标准中的更多适用容器,例如boost :: array或boost :: unordered_map。


3
投票

介绍:

STL是一个很棒的库,在很多情况下很有用,但它确实无法解决所有情况。回答STL或!STL就像回答“STL是否满足您的需求或不这样做?”

抵消STL

  • 在大多数情况下,STL有一个适合给定解决方案的容器。
  • 这是有据可查的
  • 众所周知(程序员通常已经知道它,进入项目的时间更短)
  • 它经过测试和稳定。
  • 它是跨平台的
  • 它包含在每个编译器中(不添加第3个库依赖项)
  • STL已经实施并准备就绪
  • STL闪亮,......

STL的缺点

它不需要一个简单的Graph,Red-Black Tree或一个非常复杂的元素数据库,AI需要通过量子计算机管理并发访问。事实是,STL不会,也永远不会解决所有问题。

以下几个方面只是几个例子,但它们基本上是这个事实的结果:STL是一个有限制的真实库。

  • 例外:STL中继异常,因此如果由于任何原因您无法接受异常(例如安全关键),则不能使用STL。对!异常可能被禁用,但这并不能解决STL中继它们的设计,最终会导致崩溃。
  • 需要特定(尚未包含)的数据结构:图形,树等。
  • 复杂性的特殊限制:您可能会发现STL通用容器不是最适合您的瓶颈代码。
  • 并发注意事项:要么你需要并发性而STL不提供你需要的东西(例如,由于双向[] operator,不能(容易)使用读写器锁)。您可以设计一个利用多线程的容器,以便更快地访问/搜索/插入/无论如何。
  • STL需要满足您的需求,但反过来也是如此:您需要满足STL的需求。不要尝试在带有1K非托管RAM的嵌入式微控制器中使用std::vector
  • 与其他库的兼容性:由于历史原因,您使用的库可能不接受STL(例如QtWidgets密集使用它自己的QList)。在两个方向上转换容器可能不是最佳解决方案。

实现自己的容器

读完之后,你可以想:“好吧,我确信我可以为STL的具体情况做些更好的事情。”等待!

正确实现容器变得非常迅速是一项艰巨的任务:它不仅仅是实现一些有效的工作,您可能必须:

  • 深入记录,包括限制,算法复杂性等。
  • 期待错误,并解决它们
  • 传入的额外需求:您知道,此功能缺失,类型之间的转换等。
  • 过了一会儿,你可能想要重构,并改变所有的依赖关系(太迟了?)
  • ....

像容器这样在代码深处使用的代码确实需要时间来实现,并且应该谨慎使用。


使用第三方库

不是STL并不一定意味着习惯。网络中有很多好的库,有些甚至是允许的开源许可证。

添加或不添加其他第三方库是另一个主题,但值得考虑。


2
投票

我发现在多线程代码中使用STL存在问题。即使您不跨线程共享STL对象,许多实现也使用非线程安全构造(如++用于引用计数而不是互锁增量样式,或具有非线程安全分配器)。

在每种情况下,我仍然选择使用STL并解决问题(有足够的钩子来获得你想要的东西)。

即使您选择创建自己的集合,最好遵循STL样式的迭代器,以便您可以使用仅在迭代器上运行的算法和其他STL函数。


2
投票

我看到的主要问题是必须与依赖于非投掷操作符new的遗留代码集成。


1
投票

我在大约1984年左右开始编程C并且从未使用过STL。多年来,我已经推出了自己的功能库,当STL尚未稳定或缺乏跨平台支持时,它们已经发展和成长。我的公共库已经发展到包含其他人的代码(主要是像libjpeg,libpng,ffmpeg,mysql这样的东西)和其他一些代码,我宁愿将外部代码的数量保持在最低限度。我现在确定STL很棒,但坦率地说,我对我的工具箱中的项目感到满意,并且此时不需要使用更多工具加载它。但我确实看到了新程序员通过使用STL可以实现的巨大飞跃,而无需从头开始编写所有代码。


1
投票

标准C ++反过来允许实现一些iterator operations to throw exceptions。在某些情况下,这种可能性可能会有问题。因此,您可以实现自己的简单容器,保证不会为关键操作抛出异常。


1
投票

因为几乎所有在我之前回答的人似乎都对STL容器如此热衷,所以我认为从我遇到的实际问题中编制一个不使用它们的充分理由列表是有用的。

这些可以合理地分为三大类:

1)效率低下

STL容器通常运行速度较慢,并且占用的内存太多。造成这种情况的原因部分归咎于底层数据结构和算法的过于通用的实现,额外的性能成本来自与当前任务无关的大量API必需品所需的所有额外设计约束。

鲁莽的内存使用和糟糕的性能齐头并进,因为内存由CPU以64字节为单位在缓存中进行寻址,如果你没有使用引用的局部性,你会浪费周期和宝贵的Kb缓存。

例如,std :: list每个元素需要24个字节而不是最佳4个字节。

https://lemire.me/blog/2016/09/15/the-memory-usage-of-stl-containers-can-be-surprising/

这是因为它是通过打包两个64位指针,1个int和4个字节的内存填充来实现的,而不是像分配少量连续内存那样基本做任何事情,并分别跟踪正在使用的元素,或者使用指针xor将迭代方向存储在一个指针中的技术。

https://en.wikipedia.org/wiki/XOR_linked_list

根据您的程序需求,这些低效率可以并且确实会增加大的性能命中率。

2)限制/爬行标准

当然,有时问题是您需要一些完全相同的函数或略微不同的容器类,这些容器类在STL中没有实现,例如优先级队列中的decrease_min()。

一种常见的做法是将容器包装在一个类中,并使用容器外部的额外状态和/或对容器方法的多次调用自己实现缺少的功能,这可能会模拟所需的行为,但性能要低得多,O ()复杂性高于数据结构的实际实现,因为没有办法扩展容器的内部工作。或者,您最终将两个或更多不同的容器混合在一起,因为您同时需要两个或更多在任何给定的STL容器中根本不兼容的东西,例如minmax堆,trie(因为您需要能够使用不可知的指针) )等

这些解决方案可能很丑陋并且会增加其他低效率,但语言不断发展的趋势是只添加新的STL方法来匹配C ++的特性蠕变并忽略任何缺少的核心功能。

3)并发/并行

STL容器不是线程安全的,更不是并发的。在16线程消费者CPU的当代时代,令人惊讶的是,现代语言的首选默认容器实现仍然要求你像1996年一样在每个内存访问周围编写互斥体。这对于任何非平凡的并行程序来说都是类的一个大问题,因为有内存屏障迫使线程序列化他们的执行,如果这些发生频率与STL调用相同,你可以吻你的并行性能再见。

简而言之,只要您不关心性能,内存使用,功能或并行性,STL就是好的。 STL当然仍然完美无缺,因为很多时候你不受任何这些问题的束缚,其他优先级如可读性,可移植性,可维护性或编码速度优先。


47
投票

不使用STL的主要原因是:

  1. 您的C ++实现是旧的,并且具有可怕的模板支持。
  2. 您不能使用动态内存分配。

两者在实践中都是非常罕见的要求。

对于一个长期项目来说,滚动自己的容器与STL功能重叠只会增加维护和开发成本。


26
投票

使用stl有很多好处。对于长期项目而言,收益大于成本。

  1. 新程序员能够从第一天开始理解容器,让他们有更多时间学习项目中的其他代码。 (假设他们已经像任何有能力的C ++程序员那样知道STL)
  2. 修复容器中的错误会浪费时间,浪费时间来增强业务逻辑。
  3. 很可能你不会写它们,因为无论如何都要实现STL。

话虽这么说,STL容器根本不处理并发问题。因此,在需要并发的环境中,我会使用其他容器,如英特尔TBB并发容器。这些使用细粒度锁定更加先进,这样不同的线程可以同时修改容器,并且您不必序列化对容器的访问。


15
投票

通常,我发现最好的办法是将STL与自定义分配器一起使用,而不是用手动的STL容器替换STL容器。关于STL的好处是你只需为你使用的东西付费。


6
投票

我认为这是一个典型的构建与购买场景。但是,我认为在这种情况下,我几乎总是'买',并且使用STL - 或者更好的解决方案(也许来自Boost),然后再推出自己的。您应该将大部分精力集中在应用程序的功能上,而不是它所使用的构建块上。


6
投票

我真的不这么认为。在制作我自己的容器时,我甚至会尝试将它们与STL兼容,因为通用算法的功能太大而不能放弃。 STL至少应该名义上使用,即使您所做的只是编写自己的容器并专门为它准备每个算法。这样,每个排序算法都可以调用sort(c.begin(),c.end())。如果你专门排序以产生相同的效果,即使它的工作方式不同。


5
投票

编码为Symbian。

STLPort确实支持Symbian 9,所以反对使用STL的情况比过去要弱(“它不可用”是一个非常有说服力的案例),但STL仍然对所有Symbian库都不同,所以可能比单纯的更麻烦以Symbian方式做事。

当然,基于这些理由可能会认为Symbian的编码不是“C ++编程项目”。


4
投票

我工作的大多数项目的代码库都比任何真正可用的STL版本更老 - 因此我们现在选择不引入它。


3
投票

可能发生这种情况的一种情况是,您已经在使用已经从STL提供所需功能的外部库。例如,我的公司在空间有限的区域开发应用程序,并且已经使用Qt作为窗口工具包。由于Qt提供类似STL的容器类,我们使用它们而不是将STL添加到我们的项目中。

© www.soinside.com 2019 - 2024. All rights reserved.