STL或Qt容器?

时间:2009-11-03 16:00:37

标签: c++ qt stl

使用Qt容器(QMapQVector等)对STL等效项的优缺点是什么?

我可以看到一个更喜欢Qt的理由:

  • Qt容器可以传递给Qt的其他部分。例如,它们可用于填充QVariant,然后填充QSettings(但有一些限制,只有QListQMap / QHash,其键是字符串被接受)。

还有其他吗?

编辑:假设应用程序已经依赖于Qt。

14 个答案:

答案 0 :(得分:172)

这是一个难以回答的问题。它可以归结为哲学/主观论证。

有人说......

我推荐规则“在罗马时......像罗马人那样做”

这意味着如果你在Qt土地上,代码就像Qt'ians那样。这不仅仅是为了可读性/一致性问题。考虑如果将所有内容存储在stl容器中然后必须将所有数据传递给Qt函数会发生什么。您是否真的想管理一堆将内容复制到Qt容器中的代码。你的代码已经严重依赖于Qt,所以它不像你通过使用stl容器使它更加“标准”。如果每次想要将它用于任何有用的东西时,容器的重点是什么,你必须将它复制到相应的Qt容器中吗?

答案 1 :(得分:125)

我开始只使用std::(w)string和STL容器并转换为Qt等价物,但我已经切换到QString并且我发现我正在使用Qt的容器越来越多

对于字符串,与QString相比,std::basic_string提供了更完整的功能 完全支持Unicode。它还提供efficient COW implementation,我非常依赖它。

Qt的容器:

  • 提供与QString相同的COW实现,这对于使用Qt的foreach宏非常有用 (使用元类型或信号和插槽时使用副本)。
  • 可以使用STL样式的迭代器或Java样式的迭代器
  • 可以使用QDataStream
  • 进行流式处理
  • 广泛用于Qt的API
  • 在整个操作系统中实现稳定的实施。 STL实现必须遵守C ++标准,但是 可以随意自由地做(请参阅std::string COW争议)。一些STL实现尤其如此 坏。
  • 提供哈希,除非您使用TR1
  • ,否则这些哈希不可用

QTL与STL有不同的理念,J. Blanchette well summarized:“尽管STL的容器针对原始速度进行了优化,但Qt的容器类经过精心设计,以提供方便,最少的内存使用,最小的代码扩展。“
上面的链接提供了有关QTL实现的更多细节以及使用的优化。

答案 2 :(得分:60)

Qt容器比STL容器更受限制。 STL的优势所在的几个例子(我过去曾经遇到的所有这些):

  • STL是标准化的,不会随着每个Qt版本而变化(Qt 2有QList(基于指针)和QValueList(基于值); Qt 3有QPtrListQValueList; Qt 4现在有QList,而且它完全没有像QPtrList QValueList)。
    即使您最终使用Qt容器,也请使用与STL兼容的API子集(即。push_back(),而非append(); front(),而不是first(),...)为了避免再次移植Qt 5.在Qt2-> 3和Qt3-> 4转换中,Qt容器中的变化是需要最多代码流失的那些。
  • STL双向容器都具有rbegin() / rend(),使反向迭代与前向迭代对称。并非所有Qt容器都有它们(关联的容器没有),因此反向迭代不必要地复杂化。
  • STL容器具有来自不同但兼容的迭代器类型的范围 - insert(),使std::copy()更少需要。
  • STL容器具有Allocator模板参数,与trivialQLineEdit所需的s/QString/secqstring/相比,使自定义内存管理Qt(需要typedef) })。 编辑20171220 :这样可以减少Qt在C ++ 11和C ++ 17之后的分配器设计进展,参见例如John Lakos' talkpart 2)。
  • 没有Qt等同于std::deque
  • std::listsplice()。每当我发现自己使用std::list时,都是因为我需要splice()
  • std::stackstd::queue正确聚合其基础容器,并且不会继承它,如QStackQQueue那样。
  • QSetstd::unordered_set类似,与std::set不同。
  • QListjust weird

上面的许多内容都可能是solved quite easily in Qt,但Qt中的容器库目前似乎缺乏开发重点。

EDIT 20150106 :在花了一些时间尝试将C ++ 11支持带到Qt 5容器类之后,我认为它不值得工作。如果你看看正在进入C ++标准库实现的工作,很明显Qt类永远不会赶上。我们现在已经发布了Qt 5.4,QVector 仍然不会在重新分配上移动元素,没有emplace_back()或右值 - push_back() ...我们最近还拒绝了QOptional类模板,而是等待std::optional。同样适用于std::unique_ptr。我希望这种趋势继续下去。

答案 3 :(得分:26)

让我们将这些说法分解为实际可衡量的现象:

  • 打火机:Qt容器比STL容器使用更少的内存
  • 更安全:Qt容器使用不当的机会较少
  • 更简单:Qt容器减轻了智力负担

更容易

在这种情况下提出的主张是java风格的迭代在某种程度上更容易"比STL风格,因此Qt更容易使用,因为这个额外的界面。

Java风格:

QListIterator<QString> i(list);
while (i.hasNext())
    qDebug() << i.next();

STL风格:

QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

Java迭代器样式具有更小,更清洁的优点。 问题是,这实际上不再是STL风格了。

C ++ 11 STL Style

for( auto i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

C ++ 11 foreach风格

for (QString i : list)
    qDebug << i;

这是如此简单,以至于没有理由使用其他任何东西(除非你不支持C ++ 11)。

然而,我最喜欢的是:

BOOST_FOREACH(QString i, list)
{
    qDebug << i;
}

因此,正如我们所看到的,除了一个额外的界面之外,这个界面除了一个已经时尚,流线型和现代化的界面之外什么都没有获得。在已经稳定可用的界面上添加不必要的抽象级别?不是我的想法&#34;更容易&#34;。

此外,Qt foreach和java接口增加了开销;他们复制结构,并提供不必要的间接水平。这可能看起来不多,但为什么要添加一层开销来提供一个不那么简单的接口呢? Java有这个接口,因为java没有运算符重载; C ++可以。

更安全

Qt给出的理由是隐含的共享问题,既不是隐含的也不是问题。但它确实涉及共享。

QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.

QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/

首先,这不是隐含的;你明确地将一个向量分配给另一个向量。 STL迭代器规范清楚地表明迭代器属于容器,因此我们在b和a之间清楚地引入了一个共享容器。其次,这不是一个问题;只要遵循迭代器规范的所有规则,绝对不会出错。出现问题的唯一时间是:

b.clear(); // Now the iterator i is completely invalid.

Qt指定这就好像它意味着什么,就像这个场景中出现的问题一样。它没有。迭代器无效,就像可以从多个不相交的区域访问的任何东西一样,这就是它的工作原理。事实上,Qt中的Java样式迭代器很容易实现这一点,这要归功于它严重依赖于隐式共享,这是一个反文档,如文档here所述,以及许多其他areas。这个&#34;优化&#34;似乎特别奇怪。在越来越多的多线程框架中投入使用,但这是为你做的营销。

打火机

这个有点棘手。使用写时复制和隐式共享和增长策略使得实际保证容器在任何给定时间将使用多少内存非常困难。这与STL不同,后者为您提供强大的算法保证。

我们知道the minimal bound of wasted space for a vector is the square root of the length of the vector,但似乎没有办法在Qt中实现这一点;各种&#34;优化&#34;他们支持将排除这个非常重要的节省空间的功能。 STL不需要此功能(并且大多数使用增长倍增,这更浪费),但重要的是要注意,如果需要,您至少可以实现此功能。

双链表也是如此,它可以使用XOr链接来大幅减少使用的空间。同样,由于Qt对增长和COW的要求,这对Qt来说是不可能的。

COW确实可以做得更轻,但是Intrusive Containers也是如此,例如由boost支持,而Qt在早期版本中经常使用这些容器,但它们不再使用,因为它们很难使用,不安全,并给程序员带来负担。 COW是一种不那么具有侵入性的解决方案,但由于上述原因而没有吸引力。

没有理由不能使用具有相同内存成本或低于Qt容器的STL容器,并且实际上知道在任何给定时间浪费了多少内存。遗憾的是,不可能在原始内存使用情况中比较两者,因为这些基准测试在不同的用例中会显示出截然不同的结果,这是STL旨在纠正的确切问题。

结论

尽可能避免使用Qt容器而不增加复制成本,并尽可能使用STL类型迭代(可能通过包装器或新语法)。

答案 4 :(得分:22)

STL容器:

  • 有性能保证
  • 可用于STL算法,它也具有性能保证
  • 可以被第三方C ++库(如Boost
  • )利用
  • 是标准的,可能比专有解决方案更长久
  • 鼓励算法和数据结构的通用编程。如果您编写符合STL的新算法和数据结构,您可以免费使用STL提供的内容。

答案 5 :(得分:15)

Qt容器使用写时复制习语。

答案 6 :(得分:9)

其中一个主要问题是Qt的API要求你在Qt的容器中提供数据,所以你也可以简单地使用Qt容器而不是在两者之间来回转换。

此外,如果您已经在使用Qt容器,那么单独使用它们可能会稍微优化一些,因为您不必包含STL头文件并可能链接到STL库中。但是,根据您的工具链,无论如何都可能发生这种情况。纯粹从设计的角度来看,一致性通常是件好事。

答案 7 :(得分:8)

如果您使用的数据主要用于驱动基于Qt的UI,那么一定要使用Qt容器。

如果数据主要在应用程序内部使用,并且您永远不可能远离Qt,那么除非性能问题,请使用Qt容器,因为它会使进入UI的数据位更容易处理。

如果数据主要与仅了解STL容器的其他库一起使用,则使用STL容器。如果您遇到这种情况,无论如何都会遇到麻烦,因为无论您做什么,您都会在容器类型之间来回移植。

答案 8 :(得分:7)

除了COW差异之外,STL容器在各种平台上得到更广泛的支持。如果你将你的工作限制在“主流”平台上,Qt是足够便携的,但是STL也可以在许多其他更加模糊的平台上使用(例如,德州仪器的DSP)。

因为STL是标准的而不是由单一公司控制,所以一般来说,有更多程序员可以轻松阅读,理解和修改STL代码和更多资源(书籍,在线论坛,会议等)到支持他们这样做,而不是Qt。这并不是说因为这个原因,人们应该回避Qt;就是这样,在所有其他条件相同的情况下,你应该默认为STL,但当然所有事情都很少相等,所以你必须在你自己的语境中决定最有意义的。

关于AlexKR的答案:STL性能在极限范围内得到保证,但是给定的实现可以利用与平台相关的细节来加速他们的STL。所以从这个意义上说,你可能会在不同的平台上得到不同的结果,但它永远不会比明确的保证(模数错误)慢。

答案 9 :(得分:3)

我的五美分: Qt容器应该在不同平台上工作类似。 而STL容器依赖于STL实现。 您可能会得到不同的性能结果。

修改 我并不是说STL“慢”但我指的是效果 各种实施细节。
请检查this,然后查看this
这不是STL的真正问题。很明显,如果你在性能上有显着差异,那么使用STL的代码就会出现问题。

答案 10 :(得分:3)

我想这取决于你使用Qt的方式。如果您在产品上使用它,那么使用Qt容器可能是有意义的。如果仅包含(例如)UI部分,则最好使用C ++标准容器。

答案 11 :(得分:3)

我认为STL是一个优秀的软件,但如果我要做一些KDE或Qt相关的编程,那么Qt就是要走的路。它也取决于你使用的编译器,GCC STL工作得很好但是如果你不得不使用说SUN Studio CC那么STL很可能会让你头疼,因为编译器不是STL本身。在这种情况下,因为编译器会让你头疼,只需使用Qt来省去麻烦。只是我的2美分......

答案 12 :(得分:3)

QVector有(有时)很大的限制。 It can only allocate int bytes of memory(请注意,限制以字节为单位,而不是元素数)。这意味着尝试使用QVector分配大于〜2GB的连续内存块将导致崩溃。这发生在Qt 4和5中.std :: vector没有这样的限制。

答案 13 :(得分:0)

与我一起使用STL容器的主要原因是,如果您需要自定义分配器,以便在非常大的容器中重用内存。例如,假设您有一个存储1000000个条目(键/值对)的QMap。在Qt中,无论如何都意味着恰好有1亿次分配(new次呼叫)。在STL中,您始终可以创建一个自定义分配器,在内部一次性分配所有内存,并在填充映射时将其分配给每个条目。

我的建议是在业务逻辑中编写性能关键算法时使用STL容器,然后在结果准备就绪时将它们转换回Qt容器,如果需要,可以通过UI控件和表单显示。