41

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

我想得越多,我就越意识到也许应该在某些情况下我选择不使用 STL ......例如,一个非常大的长期项目,其代码库预计将持续数年......也许精确满足项目需求的定制容器解决方案值得最初的开销吗?你怎么看,有没有什么情况下你会选择不使用 STL?

4

15 回答 15

47

不使用 STL 的主要原因是:

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

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

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

于 2008-10-06T14:28:59.883 回答
32

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

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

于 2008-10-06T15:21:18.903 回答
26

使用 stl 有很多优点。对于长期项目,收益大于成本。

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

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

于 2008-10-06T14:33:13.227 回答
15

通常,我发现最好的办法是使用带有自定义分配器的 STL,而不是用手动滚动的容器替换 STL 容器。STL 的好处是您只需为使用的内容付费。

于 2008-10-06T14:22:01.877 回答
6

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

于 2008-10-06T14:22:41.377 回答
6

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

于 2008-10-06T14:30:18.397 回答
5

Symbian 编码。

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

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

于 2008-10-07T01:05:13.233 回答
4

我参与过的大多数项目的代码库都比任何真正可用的 STL 版本都老——因此我们选择现在不介绍它。

于 2008-10-06T14:44:08.183 回答
4

介绍:

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 做得更好的事情。 ”等等!

正确地实现你的容器很快就变成了一项艰巨的任务:它不仅要实现一些可行的东西,你可能还必须:

  • 深入记录,包括限制、算法复杂度等。
  • 期待错误并解决它们
  • 传入的额外需求:您知道,缺少此功能,类型之间的转换等。
  • 一段时间后,您可能想要重构并更改所有依赖项(为时已晚?)
  • ……

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


使用第三方库

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

添加或不添加额外的 3rd 方库是另一个主题,但值得考虑。

于 2017-06-27T14:34:43.093 回答
3

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

于 2008-10-06T17:15:11.340 回答
2

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

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

即使您选择创建自己的集合,对迭代器遵循 STL 样式也是一个好主意,这样您就可以使用仅在迭代器上运行的算法和其他 STL 函数。

于 2008-10-06T14:31:53.963 回答
2

我看到的主要问题是必须与依赖非抛出 operator new 的遗留代码集成。

于 2008-10-06T15:30:55.077 回答
1

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

于 2008-10-06T15:41:24.323 回答
1

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

于 2011-10-27T12:04:29.130 回答
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 中实现,例如优先级队列中的 reduce_min()。

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

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

3)并发/并行

STL 容器不是线程安全的,更不用说并发了。在当今 16 线程消费 CPU 的时代,令人惊讶的是,现代语言的默认容器实现仍然需要您围绕每个内存访问编写互斥锁,就像 1996 年一样。对于任何非平凡的并行程序来说,这是一种重要的是,因为有内存屏障会迫使线程序列化它们的执行,如果这些发生的频率与 STL 调用相同,那么您可以告别并行性能。

简而言之,只要您不关心性能、内存使用、功能或并行性,STL 就很好。STL 当然仍然很好,因为很多时候您不受任何这些问题的约束,并且其他优先事项(如可读性、可移植性、可维护性或编码速度)优先。

于 2019-03-04T11:06:38.553 回答