【问题标题】:Why does Qt foreach create a copy of the container?为什么 Qt foreach 会创建容器的副本?
【发布时间】:2014-06-30 01:02:01
【问题描述】:

文档只是states that it does,但没有解释原因:

当容器进入一个容器时,Qt 自动获取一个副本 foreach 循环。如果您在迭代时修改容器,那 不会影响循环。 (如果不修改容器,则复制 仍然发生,但由于隐式共享复制容器 非常快。)由于 foreach 创建了容器的副本,因此使用 变量的非常量引用不允许您修改 原始容器。它只影响副本,这可能不是 你想要什么。

对我来说,这看起来像是一个自我强加的障碍,使得 Qt 的 foreach 的用处不如它本来的用途 - 现在你不能用它来修改元素。

我听说 boost 的 foreach 和新的 C++11 for (auto iter : array) 不执行复制(尽管我对它们都不熟悉)。

那么复制背后的基本原理是什么?

【问题讨论】:

  • 正确的修改方法是使用迭代器模式。
  • QT 喜欢 COW,因此他们广泛使用它来消除射中自己脚的方法。看看 C++ 标准库/boost 的迭代器失效规则,你可能会明白其中的优点。尽管如此,无论您是否依赖它,都会产生惩罚。
  • 可能是因为很多人喜欢在迭代容器的时候修改容器,导致迭代器失效
  • sashoalm,“某种”的原因就在这里,stackoverflow.com/questions/4908107/…
  • 在迭代容器时修改容器通常是错误的。只有在特定情况下才不会出错。如果您使用 Qt 容器,该副本非常便宜。如果你想修改容器,你应该直接使用迭代器,并以安全的方式使用它们。

标签: c++ qt foreach containers qtcore


【解决方案1】:

Qt 开发人员已经决定在不修改原始容器的情况下修改循环中的容器(例如在信号处理程序中,又名插槽等)时,它应该防止出现意外情况。

使用信号槽机制来追踪是否有修改可能会很棘手。如果您使用该容器成员发出信号,则基本上取决于插槽。可以肯定的是,否则您总是需要进行外部复制。

另一个优点是您可以将方法调用传递给第二个参数而无需连续重新评估,因为副本将在第一次创建。如果您问我,这实际上是一个非常简洁的功能,因为您经常希望遍历关联的数组键或值,例如 myHash.keys()myHash.values()

你可以说 boost 也有信号槽机制。是的,在我看来,这只是一种不同的方式。他们不必总是做同样的事情。 :-)

不同的人对 API、样式等有不同的品味。毕竟,您拥有所有工具来实现您计划处理的任何用例。

您也可以争辩说它可能不是您想要的,而是您会做一个明确的副本。这很公平,您可以使用 Boost 或 C++ 中的标准 foreach。

这里的副本没有性能问题,因为对于没有修改的正常迭代,写时复制(又名隐式共享)就足够了。它有一些性能开销,但可以忽略不计。

这种用例的正确 Qt 语义是使用迭代器设计模式。例如,Qt 在“Java 风格”之后到处都有迭代器类。

【讨论】:

  • 嗯,我记得有一次我浪费了多少时间试图弄清楚为什么我的更改在我走出foreach 之后就消失了。这是一个嵌套的 foreach 循环,我忘记了我使用的是 value 而不是 const-reference。我想知道我是否是唯一一个被这样烧伤的人。
  • 顺便说一句,C++11 中新的for 关键字也不会复制,对吧?
  • 我需要查找标准,但凭记忆,我会拒绝。
  • 说实话,我很惊讶复制是一种有意识的选择。我一直认为是因为技术问题需要它,或者是为了避免重新评估表达式的第二项(如果它是一个函数)。
猜你喜欢
  • 2014-10-29
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 1970-01-01
  • 2022-07-29
  • 1970-01-01
相关资源
最近更新 更多