对于STL或!STL,这就是问题所在

时间:2008-10-06 14:17:08

标签: c++ stl containers

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

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

15 个答案:

答案 0 :(得分:47)

不使用STL的主要原因是:

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

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

答案 1 :(得分:31)

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

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

答案 2 :(得分:26)

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

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

答案 3 :(得分:15)

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

答案 4 :(得分:6)

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

答案 5 :(得分:6)

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

答案 6 :(得分:5)

Symbian编码。

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

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

答案 7 :(得分:4)

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

答案 8 :(得分:3)

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

答案 9 :(得分:2)

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

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

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

答案 10 :(得分:2)

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

答案 11 :(得分:2)

<强>简介

STL是一个很棒的图书馆,在很多情况下很有用,但它绝对没有解决所有情况。回答STL或!STL就像回答&#34; STL是否满足您的需要或不是?#34;

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)。在两个方向上转换容器可能不是最好的解决方案。

实施自己的容器

读完之后,你可以想:&#34; 嗯,我确信我可以为我的具体案例做一些比STL更好的事情。&#34; WAIT!

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

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

代码中使用的代码就像容器一样,最终需要花费时间来实现,并且应该谨慎使用。

使用第三方库

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

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

答案 12 :(得分:1)

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

答案 13 :(得分:1)

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

答案 14 :(得分:0)

由于几乎每个在我之前回答的人都对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个字节的内存填充实现的,而不是像分配少量连续内存并分别跟踪正在使用的元素或使用指针异或技术将两个迭代方向存储在一个指针中。

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

根据您的程序需求,这些低效率可以而且确实会导致较大的性能损失。

2)限制/蠕变标准

当然,有时问题是您需要一些完全通用的功能或稍微不同的容器类,而这些容器类在STL中并未实现,例如优先级队列中的reduce_min()。

然后通常的做法是将容器包装在一个类中,并通过容器外部的额外状态和/或多次调用容器方法来实现缺少的功能,这可以模仿所需的行为,但性能却很高由于没有办法扩展容器的内部工作,因此它比数据结构的实际实现要低,并且O()的复杂度要高。或者,您最终将两个或多个不同的容器混在一起,因为您同时需要两个或多个在任何给定的STL容器中根本不兼容的东西,例如minmax堆,特里(因为您需要能够使用不可知的指针)等。

这些解决方案可能很丑陋,并且会增加其他效率低下的问题,但这种语言发展趋势的方式是仅添加新的STL方法以匹配C ++的特性爬行,而忽略任何缺少的核心功能。

3)并发/并行性

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

简而言之,只要您不关心性能,内存使用,功能或并行性,那么STL就是很好的选择。 当然,对于许多不受这些问题困扰的人来说,STL仍然是完美的,其他优先级(例如可读性,可移植性,可维护性或编码速度)优先。