毫无疑问,我会选择对大多数 C++ 编程项目使用 STL。然而,最近有人向我提出了这样的问题:“是否存在不使用 STL 的情况?”...

我想得越多,我就越意识到也许在某些情况下我应该选择不使用STL......例如,一个非常大的长期项目,其代码库预计将持续数年......也许一个完全符合项目需求的定制容器解决方案值得最初的开销?您认为,是否存在您会选择不使用 STL 的情况?

有帮助吗?

解决方案

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

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

其他提示

不使用STL的主要原因是:

  1. 您的C ++实现已经过时,并且支持可怕的模板。
  2. 您无法使用动态内存分配。
  3. 两者在实践中都是非常罕见的要求。

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

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

  1. 新程序员能够从第一天开始理解容器,让他们有更多时间学习项目中的其他代码。 (假设他们已经像任何有能力的C ++程序员那样知道STL)
  2. 修复容器中的错误会浪费时间,浪费时间来增强业务逻辑。
  3. 最有可能的是你不打算写它们,无论如何都要实施STL。
  4. 话虽如此,STL容器根本不处理并发问题。因此,在需要并发的环境中,我会使用其他容器,如英特尔TBB并发容器。使用细粒度锁定这些更高级,这样不同的线程可以同时修改容器,而不必序列化对容器的访问。

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

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

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

Symbian编码。

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

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

我参与过的大多数项目的代码库比任何真正可用的STL版本都要旧 - 因此我们现在选择不引入它。

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

介绍:

STL 是一个很棒的库,在很多情况下都很有用,但它绝对不能解决所有情况。回答 STL 或 !STL 就像回答“STL 是否满足您的需求?”

STL的优点

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

STL的对比

无论您需要一个简单的图、红黑树还是一个非常复杂的元素数据库,以及通过量子计算机管理并发访问的人工智能,都没关系。事实是,STL 没有、也永远不会解决所有问题。

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

  • 例外情况:STL 中继异常,因此如果出于任何原因您不能接受异常(例如安全关键),您不能使用 STL。正确的!异常可能会被禁用,但这并不能解决依赖它们的 STL 的设计问题,并且最终会导致崩溃。

  • 需要特定的(尚未包括)数据结构:图表、树等

  • 复杂性的特殊约束:您可能会发现 STL 通用容器并不是最适合您的瓶颈代码。

  • 并发注意事项:要么你需要并发性,而 STL 不能提供你所需要的(例如由于双向锁,读写锁不能(轻易)使用 [] operator)。您可以设计一个利用多线程的容器,以实现更快的访问/搜索/插入/任何操作。

  • STL 需要满足您的需求,但反之亦然:你需要满足STL的需求。不要尝试使用 std::vector 在具有 1K 非托管 RAM 的嵌入式微控制器中。

  • 与其他库的兼容性:可能由于历史原因,您使用的库不接受 STL(例如QtWidgets 大量使用它自己的 QList)。双向转换容器可能不是最佳解决方案。


实施您自己的容器

读完之后,你可能会想:”嗯,我确信对于我的具体情况,我可能会比 STL 做得更好。“ 等待!

正确实现容器很快就会成为一项艰巨的任务:这不仅仅是实现一些有效的东西,你可能必须:

  • 深入记录它,包括局限性、算法复杂性等。
  • 预期错误并解决它们
  • 传入的额外需求:你知道,这个函数缺失,类型之间的转换等等。
  • 一段时间后,您可能想要重构并更改所有依赖项(太晚了?)
  • ....

像容器一样在代码深处使用的代码绝对需要时间来实现,并且应该小心。


使用第 3 方库

不是STL并不一定意味着定制。网络上有很多好的库,有些甚至拥有宽松的开源许可证。

添加或不添加额外的第三方库是另一个话题,但值得考虑。

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

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

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

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

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

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

由于几乎所有在我之前回答的人似乎都热衷于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当然仍然完美无缺,因为您不受任何这些问题的束缚,其他优先级如可读性,可移植性,可维护性或编码速度先于

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top